Move system calls to the SDK and normalize their names
[monolithium.git] / kernel / src / sync.c
1 /*
2  * sync.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 <sync.h>
21 #include <thread.h>
22 #include <heap.h>
23
24 void enter_critical(critical_t *critical)
25 {
26     *critical = 0;
27     if (disable_ints()) *critical |= (1 << 0);
28     if (scheduler_enabled) *critical |= (1 << 1);
29
30     disable_ints();
31     scheduler_enabled = FALSE;
32 }
33
34 void leave_critical(critical_t *critical)
35 {
36     if (*critical & (1 << 1)) scheduler_enabled = TRUE;
37     if (*critical & (1 << 0)) enable_ints();
38 }
39
40 void acquire_lock(lock_t *lock)
41 {
42     if (scheduler_enabled)
43     {
44         while (__sync_lock_test_and_set(lock, 1))
45         {
46             scheduler_wait(WAIT_UNTIL_EQUAL, NO_TIMEOUT, (uintptr_t*)lock, 0);
47         }
48     }
49     else
50     {
51         while (__sync_lock_test_and_set(lock, 1)) continue;
52     }
53 }
54
55 void release_lock(lock_t *lock)
56 {
57     __sync_lock_release(lock);
58     if (scheduler_enabled) syscall_yield_quantum();
59 }
60
61 void acquire_resource_shared(resource_t *res)
62 {
63     if (scheduler_enabled)
64     {
65         while (__sync_val_compare_and_swap(res, 0, 1) == 2)
66         {
67             scheduler_wait(WAIT_UNTIL_NOT_EQUAL, NO_TIMEOUT, (uintptr_t*)res, 2);
68         }
69     }
70     else
71     {
72         while (__sync_val_compare_and_swap(res, 0, 1) == 2) continue;
73     }
74 }
75
76 void acquire_resource_exclusive(resource_t *res)
77 {
78     if (scheduler_enabled)
79     {
80         while (!__sync_bool_compare_and_swap(res, 0, 2))
81         {
82             scheduler_wait(WAIT_UNTIL_EQUAL, NO_TIMEOUT, (uintptr_t*)res, 0);
83         }
84     }
85     else
86     {
87         while (!__sync_bool_compare_and_swap(res, 0, 2)) continue;
88     }
89 }
90
91 void release_resource(resource_t *res)
92 {
93     __sync_lock_release(res);
94     if (scheduler_enabled) syscall_yield_quantum();
95 }
96
97 void init_semaphore(semaphore_t *semaphore, dword_t init_count, dword_t max_count)
98 {
99     ASSERT(init_count <= max_count);
100
101     semaphore->count = init_count;
102     semaphore->max_count = max_count;
103 }
104
105 sysret_t syscall_create_semaphore(const char *name, dword_t init_count, dword_t max_count, handle_t *handle)
106 {
107     handle_t safe_handle;
108     char *safe_name = NULL;
109     if ((max_count == 0) || (init_count > max_count)) return ERR_INVALID;
110
111     semaphore_t *semaphore = (semaphore_t*)malloc(sizeof(semaphore_t));
112     if (semaphore == NULL) return ERR_NOMEMORY;
113
114     if (name != NULL)
115     {
116         if (get_previous_mode() == USER_MODE) safe_name = copy_user_string(name);
117         else safe_name = (char*)name;
118     }
119
120     init_semaphore(semaphore, init_count, max_count);
121
122     semaphore->header.name = strdup(safe_name);
123     semaphore->header.type = OBJECT_SEMAPHORE;
124
125     dword_t ret = create_object(&semaphore->header);
126     if (ret == ERR_SUCCESS)
127     {
128         ret = open_object(&semaphore->header, 0, &safe_handle);
129         dereference(&semaphore->header);
130     }
131
132     if (ret != ERR_SUCCESS)
133     {
134         if (semaphore->header.name) free(semaphore->header.name);
135         free(semaphore);
136     }
137
138     EH_TRY *handle = safe_handle;
139     EH_CATCH ret = ERR_BADPTR;
140     EH_DONE;
141
142     if (get_previous_mode() == USER_MODE) free(safe_name);
143     return ret;
144 }
145
146 sysret_t syscall_open_semaphore(const char *name, handle_t *handle)
147 {
148     handle_t safe_handle;
149     char *safe_name = NULL;
150
151     if (get_previous_mode() == USER_MODE)
152     {
153         dword_t name_length = 0;
154
155         EH_TRY name_length = strlen(name);
156         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
157         EH_DONE;
158
159         if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
160         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
161
162         safe_name = copy_user_string(name);
163         if (safe_name == NULL) return ERR_BADPTR;
164     }
165     else safe_name = (char*)name;
166
167     dword_t ret = open_object_by_name(safe_name, OBJECT_SEMAPHORE, 0, &safe_handle);
168
169     EH_TRY *handle = safe_handle;
170     EH_CATCH
171     {
172         syscall_close_object(safe_handle);
173         ret = ERR_BADPTR;
174     }
175     EH_DONE;
176
177     if (get_previous_mode() == USER_MODE) free(safe_name);
178     return ret;
179 }
180
181 dword_t wait_semaphore(semaphore_t *semaphore, dword_t count, dword_t timeout)
182 {
183     if (count > semaphore->max_count) return ERR_INVALID;
184
185     if (semaphore->count < count)
186     {
187         dword_t ret = scheduler_wait(WAIT_UNTIL_NOT_LESS, timeout, &semaphore->count, count);
188
189         if (ret == WAIT_TIMED_OUT) return ERR_TIMEOUT;
190         else if (ret == WAIT_CANCELED) return ERR_CANCELED;
191     }
192
193     semaphore->count -= count;
194     return ERR_SUCCESS;
195 }
196
197 dword_t release_semaphore(semaphore_t *semaphore, dword_t count)
198 {
199     if ((semaphore->count + count) <= semaphore->max_count)
200     {
201         semaphore->count += count;
202         if (scheduler_enabled) syscall_yield_quantum();
203         return ERR_SUCCESS;
204     }
205     else
206     {
207         return ERR_INVALID;
208     }
209 }
210
211 sysret_t syscall_wait_semaphore(handle_t semaphore, dword_t count, dword_t timeout)
212 {
213     semaphore_t *obj;
214
215     if (!reference_by_handle(semaphore, OBJECT_SEMAPHORE, (object_t**)&obj)) return ERR_INVALID;
216     dword_t ret = wait_semaphore(obj, count, timeout);
217     dereference(&obj->header);
218
219     return ret;
220 }
221
222 sysret_t syscall_release_semaphore(handle_t semaphore, dword_t count)
223 {
224     semaphore_t *obj;
225
226     if (!reference_by_handle(semaphore, OBJECT_SEMAPHORE, (object_t**)&obj)) return ERR_INVALID;
227     release_semaphore(obj, count);
228     dereference(&obj->header);
229
230     return ERR_SUCCESS;
231 }