30bf9f46b233d2ef8104da99b17b52927c270c81
[monolithium.git] / kernel / src / semaphore.c
1 /*
2  * semaphore.c
3  *
4  * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <semaphore.h>
21 #include <thread.h>
22 #include <heap.h>
23
24 void init_semaphore(semaphore_t *semaphore, dword_t init_count, dword_t max_count)
25 {
26     ASSERT(init_count <= max_count);
27
28     semaphore->count = init_count;
29     semaphore->max_count = max_count;
30 }
31
32 sysret_t syscall_create_semaphore(const char *name, dword_t init_count, dword_t max_count, handle_t *handle)
33 {
34     handle_t safe_handle;
35     char *safe_name = NULL;
36     if ((max_count == 0) || (init_count > max_count)) return ERR_INVALID;
37
38     semaphore_t *semaphore = (semaphore_t*)malloc(sizeof(semaphore_t));
39     if (semaphore == NULL) return ERR_NOMEMORY;
40
41     if (name != NULL)
42     {
43         if (get_previous_mode() == USER_MODE) safe_name = copy_user_string(name);
44         else safe_name = (char*)name;
45     }
46
47     init_semaphore(semaphore, init_count, max_count);
48     init_object(&semaphore->header, name, OBJECT_SEMAPHORE);
49
50     dword_t ret = create_object(&semaphore->header);
51     if (ret != ERR_SUCCESS)
52     {
53         if (semaphore->header.name) free(semaphore->header.name);
54         free(semaphore);
55         return ret;
56     }
57
58     ret = open_object(&semaphore->header, 0, &safe_handle);
59     dereference(&semaphore->header);
60
61     if (ret == ERR_SUCCESS)
62     {
63         EH_TRY *handle = safe_handle;
64         EH_CATCH ret = ERR_BADPTR;
65         EH_DONE;
66     }
67
68     if (get_previous_mode() == USER_MODE) free(safe_name);
69     return ret;
70 }
71
72 sysret_t syscall_open_semaphore(const char *name, handle_t *handle)
73 {
74     handle_t safe_handle;
75     char *safe_name = NULL;
76
77     if (get_previous_mode() == USER_MODE)
78     {
79         dword_t name_length = 0;
80
81         EH_TRY name_length = strlen(name);
82         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
83         EH_DONE;
84
85         if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
86         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
87
88         safe_name = copy_user_string(name);
89         if (safe_name == NULL) return ERR_BADPTR;
90     }
91     else safe_name = (char*)name;
92
93     dword_t ret = open_object_by_name(safe_name, OBJECT_SEMAPHORE, 0, &safe_handle);
94
95     EH_TRY *handle = safe_handle;
96     EH_CATCH
97     {
98         syscall_close_object(safe_handle);
99         ret = ERR_BADPTR;
100     }
101     EH_DONE;
102
103     if (get_previous_mode() == USER_MODE) free(safe_name);
104     return ret;
105 }
106
107 dword_t wait_semaphore(semaphore_t *semaphore, dword_t count, dword_t timeout)
108 {
109     if (count > semaphore->max_count) return ERR_INVALID;
110
111     if (semaphore->count < count)
112     {
113         dword_t ret = scheduler_wait(WAIT_UNTIL_NOT_LESS, timeout, &semaphore->count, count);
114
115         if (ret == WAIT_TIMED_OUT) return ERR_TIMEOUT;
116         else if (ret == WAIT_CANCELED) return ERR_CANCELED;
117     }
118
119     semaphore->count -= count;
120     return ERR_SUCCESS;
121 }
122
123 dword_t release_semaphore(semaphore_t *semaphore, dword_t count)
124 {
125     if ((semaphore->count + count) <= semaphore->max_count)
126     {
127         semaphore->count += count;
128         if (scheduler_enabled) syscall_yield_quantum();
129         return ERR_SUCCESS;
130     }
131     else
132     {
133         return ERR_INVALID;
134     }
135 }
136
137 sysret_t syscall_wait_semaphore(handle_t semaphore, dword_t count, dword_t timeout)
138 {
139     semaphore_t *obj;
140
141     if (!reference_by_handle(semaphore, OBJECT_SEMAPHORE, (object_t**)&obj)) return ERR_INVALID;
142     dword_t ret = wait_semaphore(obj, count, timeout);
143     dereference(&obj->header);
144
145     return ret;
146 }
147
148 sysret_t syscall_release_semaphore(handle_t semaphore, dword_t count)
149 {
150     semaphore_t *obj;
151
152     if (!reference_by_handle(semaphore, OBJECT_SEMAPHORE, (object_t**)&obj)) return ERR_INVALID;
153     release_semaphore(obj, count);
154     dereference(&obj->header);
155
156     return ERR_SUCCESS;
157 }