1 /*
2 * Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
3 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7 #include <semaphore.h>
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdarg.h>
12 #include <stdlib.h>
13 #include <pthread.h>
14
15 #include <OS.h>
16
17 #include <AutoDeleter.h>
18 #include <errno_private.h>
19 #include <posix/realtime_sem_defs.h>
20 #include <syscall_utils.h>
21 #include <syscalls.h>
22 #include <time_private.h>
23 #include <user_mutex_defs.h>
24
25
26 #define SEM_TYPE_NAMED 1
27 #define SEM_TYPE_UNNAMED 2
28 #define SEM_TYPE_UNNAMED_SHARED 3
29
30
31 static int32
atomic_add_if_greater(int32 * value,int32 amount,int32 testValue)32 atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
33 {
34 int32 current = atomic_get(value);
35 while (current > testValue) {
36 int32 old = atomic_test_and_set(value, current + amount, current);
37 if (old == current)
38 return old;
39 current = old;
40 }
41 return current;
42 }
43
44
45 sem_t*
sem_open(const char * name,int openFlags,...)46 sem_open(const char* name, int openFlags,...)
47 {
48 if (name == NULL) {
49 __set_errno(B_BAD_VALUE);
50 return SEM_FAILED;
51 }
52
53 // get the mode and semaphore count parameters, if O_CREAT is specified
54 mode_t mode = 0;
55 unsigned semCount = 0;
56
57 if ((openFlags & O_CREAT) != 0) {
58 va_list args;
59 va_start(args, openFlags);
60 mode = va_arg(args, mode_t);
61 semCount = va_arg(args, unsigned);
62 va_end(args);
63 } else {
64 // clear O_EXCL, if O_CREAT is not given
65 openFlags &= ~O_EXCL;
66 }
67
68 // Allocate a sem_t structure -- we don't know, whether this is the first
69 // call of this process to open the semaphore. If it is, we will keep the
70 // structure, otherwise we will delete it later.
71 sem_t* sem = (sem_t*)malloc(sizeof(sem_t));
72 if (sem == NULL) {
73 __set_errno(B_NO_MEMORY);
74 return SEM_FAILED;
75 }
76
77 sem->type = SEM_TYPE_NAMED;
78 MemoryDeleter semDeleter(sem);
79
80 // ask the kernel to open the semaphore
81 sem_t* usedSem;
82 status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount,
83 sem, &usedSem);
84 if (error != B_OK) {
85 __set_errno(error);
86 return SEM_FAILED;
87 }
88
89 if (usedSem == sem)
90 semDeleter.Detach();
91
92 return usedSem;
93 }
94
95
96 int
sem_close(sem_t * semaphore)97 sem_close(sem_t* semaphore)
98 {
99 sem_t* deleteSem = NULL;
100 status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id,
101 &deleteSem);
102 if (error == B_OK)
103 free(deleteSem);
104
105 RETURN_AND_SET_ERRNO(error);
106 }
107
108
109 int
sem_unlink(const char * name)110 sem_unlink(const char* name)
111 {
112 RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
113 }
114
115
116 int
sem_init(sem_t * semaphore,int shared,unsigned value)117 sem_init(sem_t* semaphore, int shared, unsigned value)
118 {
119 semaphore->type = shared ? SEM_TYPE_UNNAMED_SHARED : SEM_TYPE_UNNAMED;
120 semaphore->u.unnamed_sem = value;
121 return 0;
122 }
123
124
125 int
sem_destroy(sem_t * semaphore)126 sem_destroy(sem_t* semaphore)
127 {
128 if (semaphore->type != SEM_TYPE_UNNAMED && semaphore->type != SEM_TYPE_UNNAMED_SHARED)
129 RETURN_AND_SET_ERRNO(EINVAL);
130
131 return 0;
132 }
133
134
135 static int
unnamed_sem_post(sem_t * semaphore)136 unnamed_sem_post(sem_t* semaphore)
137 {
138 int32* sem = (int32*)&semaphore->u.unnamed_sem;
139 int32 oldValue = atomic_add_if_greater(sem, 1, -1);
140 if (oldValue > -1)
141 return 0;
142
143 uint32 flags = 0;
144 if (semaphore->type == SEM_TYPE_UNNAMED_SHARED)
145 flags |= B_USER_MUTEX_SHARED;
146
147 return _kern_mutex_sem_release(sem, flags);
148 }
149
150
151 static int
unnamed_sem_trywait(sem_t * semaphore)152 unnamed_sem_trywait(sem_t* semaphore)
153 {
154 int32* sem = (int32*)&semaphore->u.unnamed_sem;
155 int32 oldValue = atomic_add_if_greater(sem, -1, 0);
156 if (oldValue > 0)
157 return 0;
158
159 return EAGAIN;
160 }
161
162
163 static int
unnamed_sem_timedwait(sem_t * semaphore,clockid_t clock_id,const struct timespec * timeout)164 unnamed_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
165 const struct timespec* timeout)
166 {
167 int32* sem = (int32*)&semaphore->u.unnamed_sem;
168
169 bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
170 uint32 flags = 0;
171 if (semaphore->type == SEM_TYPE_UNNAMED_SHARED)
172 flags |= B_USER_MUTEX_SHARED;
173 if (timeout != NULL) {
174 if (!timespec_to_bigtime(*timeout, timeoutMicros))
175 timeoutMicros = -1;
176
177 switch (clock_id) {
178 case CLOCK_REALTIME:
179 flags |= B_ABSOLUTE_REAL_TIME_TIMEOUT;
180 break;
181 case CLOCK_MONOTONIC:
182 flags |= B_ABSOLUTE_TIMEOUT;
183 break;
184 default:
185 return EINVAL;
186 }
187 }
188
189 int result = unnamed_sem_trywait(semaphore);
190 if (result == 0)
191 return 0;
192 if (timeoutMicros < 0)
193 return EINVAL;
194
195 return _kern_mutex_sem_acquire(sem, NULL, flags, timeoutMicros);
196 }
197
198
199 int
sem_post(sem_t * semaphore)200 sem_post(sem_t* semaphore)
201 {
202 status_t error;
203 if (semaphore->type == SEM_TYPE_NAMED)
204 error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
205 else
206 error = unnamed_sem_post(semaphore);
207
208 RETURN_AND_SET_ERRNO(error);
209 }
210
211
212 static int
named_sem_timedwait(sem_t * semaphore,clockid_t clock_id,const struct timespec * timeout)213 named_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
214 const struct timespec* timeout)
215 {
216 bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
217 uint32 flags = 0;
218 if (timeout != NULL) {
219 if (!timespec_to_bigtime(*timeout, timeoutMicros)) {
220 status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
221 B_RELATIVE_TIMEOUT, 0);
222 if (err == B_WOULD_BLOCK)
223 err = EINVAL;
224 // do nothing, return err as it is.
225 return err;
226 }
227
228 switch (clock_id) {
229 case CLOCK_REALTIME:
230 flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
231 break;
232 case CLOCK_MONOTONIC:
233 flags = B_ABSOLUTE_TIMEOUT;
234 break;
235 default:
236 return EINVAL;
237 }
238 }
239
240 status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, flags,
241 timeoutMicros);
242 if (err == B_WOULD_BLOCK)
243 err = ETIMEDOUT;
244
245 return err;
246 }
247
248
249 int
sem_trywait(sem_t * semaphore)250 sem_trywait(sem_t* semaphore)
251 {
252 status_t error;
253 if (semaphore->type == SEM_TYPE_NAMED) {
254 error = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
255 B_RELATIVE_TIMEOUT, 0);
256 } else
257 error = unnamed_sem_trywait(semaphore);
258
259 RETURN_AND_SET_ERRNO(error);
260 }
261
262
263 int
sem_wait(sem_t * semaphore)264 sem_wait(sem_t* semaphore)
265 {
266 status_t error;
267 if (semaphore->type == SEM_TYPE_NAMED)
268 error = named_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
269 else
270 error = unnamed_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
271
272 RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
273 }
274
275
276 int
sem_clockwait(sem_t * semaphore,clockid_t clock_id,const struct timespec * abstime)277 sem_clockwait(sem_t* semaphore, clockid_t clock_id, const struct timespec* abstime)
278 {
279 status_t error;
280 if (semaphore->type == SEM_TYPE_NAMED)
281 error = named_sem_timedwait(semaphore, clock_id, abstime);
282 else
283 error = unnamed_sem_timedwait(semaphore, clock_id, abstime);
284
285 RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
286 }
287
288
289 int
sem_timedwait(sem_t * semaphore,const struct timespec * abstime)290 sem_timedwait(sem_t* semaphore, const struct timespec* abstime)
291 {
292 return sem_clockwait(semaphore, CLOCK_REALTIME, abstime);
293 }
294
295
296 int
sem_getvalue(sem_t * semaphore,int * value)297 sem_getvalue(sem_t* semaphore, int* value)
298 {
299 if (semaphore->type == SEM_TYPE_NAMED) {
300 RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
301 semaphore->u.named_sem_id, value));
302 } else {
303 *value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
304 return 0;
305 }
306 }
307