Improve context switching and scheduler APIs.
[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         wait_condition_t condition = { .type = WAIT_UNTIL_NOT_LESS, .pointer = &semaphore->count, .value = count };
114         wait_result_t result = scheduler_wait(&condition, timeout);
115
116         if (result == WAIT_TIMED_OUT) return ERR_TIMEOUT;
117         else if (result == WAIT_CANCELED) return ERR_CANCELED;
118     }
119
120     semaphore->count -= count;
121     return ERR_SUCCESS;
122 }
123
124 dword_t release_semaphore(semaphore_t *semaphore, dword_t count)
125 {
126     if ((semaphore->count + count) <= semaphore->max_count)
127     {
128         semaphore->count += count;
129         if (scheduler_enabled) syscall_yield_quantum();
130         return ERR_SUCCESS;
131     }
132     else
133     {
134         return ERR_INVALID;
135     }
136 }
137
138 sysret_t syscall_wait_semaphore(handle_t semaphore, dword_t count, dword_t timeout)
139 {
140     semaphore_t *obj;
141
142     if (!reference_by_handle(semaphore, OBJECT_SEMAPHORE, (object_t**)&obj)) return ERR_INVALID;
143     dword_t ret = wait_semaphore(obj, count, timeout);
144     dereference(&obj->header);
145
146     return ret;
147 }
148
149 sysret_t syscall_release_semaphore(handle_t semaphore, dword_t count)
150 {
151     semaphore_t *obj;
152
153     if (!reference_by_handle(semaphore, OBJECT_SEMAPHORE, (object_t**)&obj)) return ERR_INVALID;
154     release_semaphore(obj, count);
155     dereference(&obj->header);
156
157     return ERR_SUCCESS;
158 }