sem.c (dd554467bb5560004a538e60c8aa7ebb1f795be2) sem.c (bf4dc8772e44b94cd0e14387fd6049aded8f061e)
1/*
1/*
2 * Copyright (C) 2010 David Xu <davidxu@freebsd.org>.
3 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice(s), this list of conditions and the following disclaimer as

--- 14 unchanged lines hidden (view full) ---

25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $FreeBSD$
31 */
32
2 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice(s), this list of conditions and the following disclaimer as

--- 14 unchanged lines hidden (view full) ---

24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * $FreeBSD$
30 */
31
33/*
34 * Some notes about this implementation.
35 *
36 * This is mostly a simple implementation of POSIX semaphores that
37 * does not need threading. Any semaphore created is a kernel-based
38 * semaphore regardless of the pshared attribute. This is necessary
39 * because libc's stub for pthread_cond_wait() doesn't really wait,
40 * and it is not worth the effort impose this behavior on libc.
41 *
42 * All functions here are designed to be thread-safe so that a
43 * threads library need not provide wrappers except to make
44 * sem_wait() and sem_timedwait() cancellation points or to
45 * provide a faster userland implementation for non-pshared
46 * semaphores.
47 *
48 * Also, this implementation of semaphores cannot really support
49 * real pshared semaphores. The sem_t is an allocated object
50 * and can't be seen by other processes when placed in shared
51 * memory. It should work across forks as long as the semaphore
52 * is created before any forks.
53 *
54 * The function sem_init() should be overridden by a threads
55 * library if it wants to provide a different userland version
56 * of semaphores. The functions sem_wait() and sem_timedwait()
57 * need to be wrapped to provide cancellation points. The function
58 * sem_post() may need to be wrapped to be signal-safe.
59 */
60#include "namespace.h"
61#include <sys/types.h>
62#include <sys/queue.h>
63#include <machine/atomic.h>
32#include <stdlib.h>
64#include <errno.h>
33#include <errno.h>
65#include <sys/umtx.h>
66#include <sys/_semaphore.h>
67#include <limits.h>
68#include <fcntl.h>
34#include <fcntl.h>
69#include <pthread.h>
35#include <semaphore.h>
70#include <stdarg.h>
36#include <stdarg.h>
71#include <stdlib.h>
72#include <time.h>
73#include "un-namespace.h"
74#include "libc_private.h"
37#include <pthread.h>
38#include <sys/queue.h>
39#include <_semaphore.h>
75
40
76/*
77 * Old semaphore definitions.
78 */
79struct sem {
80#define SEM_MAGIC ((u_int32_t) 0x09fa4012)
81 u_int32_t magic;
82 pthread_mutex_t lock;
83 pthread_cond_t gtzero;
84 u_int32_t count;
85 u_int32_t nwaiters;
86#define SEM_USER (NULL)
87 semid_t semid; /* semaphore id if kernel (shared) semaphore */
88 int syssem; /* 1 if kernel (shared) semaphore */
89 LIST_ENTRY(sem) entry;
90 struct sem **backpointer;
91};
41#define _SEM_CHECK_VALIDITY(sem) \
42 if ((*(sem))->magic != SEM_MAGIC) { \
43 errno = EINVAL; \
44 retval = -1; \
45 goto RETURN; \
46 }
92
47
93typedef struct sem* sem_t;
94
95#define SEM_FAILED ((sem_t *)0)
96#define SEM_VALUE_MAX __INT_MAX
97
98#define SYM_FB10(sym) __CONCAT(sym, _fb10)
99#define WEAK_REF(sym, alias) __weak_reference(sym, alias)
100#define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver)
101
102#define FB10_COMPAT(func, sym) \
103 WEAK_REF(func, SYM_FB10(sym)); \
104 SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0)
105
106static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
48static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem);
107static void sem_free(sem_t sem);
49static void sem_free(sem_t sem);
108
50
109static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems);
51static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems);
110static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
111
52static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER;
53
112FB10_COMPAT(_libc_sem_init_compat, sem_init);
113FB10_COMPAT(_libc_sem_destroy_compat, sem_destroy);
114FB10_COMPAT(_libc_sem_open_compat, sem_open);
115FB10_COMPAT(_libc_sem_close_compat, sem_close);
116FB10_COMPAT(_libc_sem_unlink_compat, sem_unlink);
117FB10_COMPAT(_libc_sem_wait_compat, sem_wait);
118FB10_COMPAT(_libc_sem_trywait_compat, sem_trywait);
119FB10_COMPAT(_libc_sem_timedwait_compat, sem_timedwait);
120FB10_COMPAT(_libc_sem_post_compat, sem_post);
121FB10_COMPAT(_libc_sem_getvalue_compat, sem_getvalue);
122
123static inline int
124sem_check_validity(sem_t *sem)
125{
126
127 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
128 return (0);
129 else {
130 errno = EINVAL;
131 return (-1);
132 }
133}
134
135static void
136sem_free(sem_t sem)
137{
138
54static void
55sem_free(sem_t sem)
56{
57
58 _pthread_mutex_destroy(&sem->lock);
59 _pthread_cond_destroy(&sem->gtzero);
139 sem->magic = 0;
140 free(sem);
141}
142
143static sem_t
144sem_alloc(unsigned int value, semid_t semid, int system_sem)
145{
146 sem_t sem;

--- 4 unchanged lines hidden (view full) ---

151 }
152
153 sem = (sem_t)malloc(sizeof(struct sem));
154 if (sem == NULL) {
155 errno = ENOSPC;
156 return (NULL);
157 }
158
60 sem->magic = 0;
61 free(sem);
62}
63
64static sem_t
65sem_alloc(unsigned int value, semid_t semid, int system_sem)
66{
67 sem_t sem;

--- 4 unchanged lines hidden (view full) ---

72 }
73
74 sem = (sem_t)malloc(sizeof(struct sem));
75 if (sem == NULL) {
76 errno = ENOSPC;
77 return (NULL);
78 }
79
80 /*
81 * Initialize the semaphore.
82 */
83 if (_pthread_mutex_init(&sem->lock, NULL) != 0) {
84 free(sem);
85 errno = ENOSPC;
86 return (NULL);
87 }
88
89 if (_pthread_cond_init(&sem->gtzero, NULL) != 0) {
90 _pthread_mutex_destroy(&sem->lock);
91 free(sem);
92 errno = ENOSPC;
93 return (NULL);
94 }
95
159 sem->count = (u_int32_t)value;
160 sem->nwaiters = 0;
161 sem->magic = SEM_MAGIC;
162 sem->semid = semid;
163 sem->syssem = system_sem;
164 return (sem);
165}
166
167int
96 sem->count = (u_int32_t)value;
97 sem->nwaiters = 0;
98 sem->magic = SEM_MAGIC;
99 sem->semid = semid;
100 sem->syssem = system_sem;
101 return (sem);
102}
103
104int
168_libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value)
105sem_init(sem_t *sem, int pshared, unsigned int value)
169{
106{
170 semid_t semid;
107 int retval, got_system_sem;
108 semid_t semid;
171
109
110 got_system_sem = 0;
111 semid = SEM_USER;
172 /*
112 /*
173 * We always have to create the kernel semaphore if the
174 * threads library isn't present since libc's version of
175 * pthread_cond_wait() is just a stub that doesn't really
176 * wait.
113 * Range check the arguments.
177 */
114 */
178 semid = (semid_t)SEM_USER;
179 if ((pshared != 0) && ksem_init(&semid, value) != 0)
180 return (-1);
181
182 *sem = sem_alloc(value, semid, pshared);
183 if ((*sem) == NULL) {
184 if (pshared != 0)
185 ksem_destroy(semid);
186 return (-1);
115 if (pshared != 0) {
116 retval = ksem_init(&semid, value);
117 if (retval == -1)
118 goto RETURN;
119 got_system_sem = 1;
187 }
120 }
188 return (0);
121
122 (*sem) = sem_alloc(value, semid, got_system_sem);
123 if ((*sem) == NULL)
124 retval = -1;
125 else
126 retval = 0;
127 RETURN:
128 if (retval != 0 && got_system_sem)
129 ksem_destroy(semid);
130 return retval;
189}
190
191int
131}
132
133int
192_libc_sem_destroy_compat(sem_t *sem)
134sem_destroy(sem_t *sem)
193{
135{
194 int retval;
136 int retval;
137
138 _SEM_CHECK_VALIDITY(sem);
195
139
196 if (sem_check_validity(sem) != 0)
197 return (-1);
198
140 _pthread_mutex_lock(&(*sem)->lock);
199 /*
200 * If this is a system semaphore let the kernel track it otherwise
201 * make sure there are no waiters.
202 */
141 /*
142 * If this is a system semaphore let the kernel track it otherwise
143 * make sure there are no waiters.
144 */
203 if ((*sem)->syssem != 0)
145 if ((*sem)->syssem != 0) {
204 retval = ksem_destroy((*sem)->semid);
146 retval = ksem_destroy((*sem)->semid);
205 else if ((*sem)->nwaiters > 0) {
147 if (retval == -1) {
148 _pthread_mutex_unlock(&(*sem)->lock);
149 goto RETURN;
150 }
151 } else if ((*sem)->nwaiters > 0) {
152 _pthread_mutex_unlock(&(*sem)->lock);
206 errno = EBUSY;
207 retval = -1;
153 errno = EBUSY;
154 retval = -1;
155 goto RETURN;
208 }
156 }
209 else {
210 retval = 0;
211 (*sem)->magic = 0;
212 }
157 _pthread_mutex_unlock(&(*sem)->lock);
213
158
214 if (retval == 0)
215 sem_free(*sem);
216 return (retval);
159 sem_free(*sem);
160
161 retval = 0;
162 RETURN:
163 return retval;
217}
218
219sem_t *
164}
165
166sem_t *
220_libc_sem_open_compat(const char *name, int oflag, ...)
167sem_open(const char *name, int oflag, ...)
221{
222 sem_t *sem;
223 sem_t s;
224 semid_t semid;
225 mode_t mode;
226 unsigned int value;
227
228 mode = 0;

--- 14 unchanged lines hidden (view full) ---

243 if (ksem_open(&semid, name, oflag, mode, value) == -1)
244 return (SEM_FAILED);
245 /*
246 * search for a duplicate ID, we must return the same sem_t *
247 * if we locate one.
248 */
249 _pthread_mutex_lock(&named_sems_mtx);
250 LIST_FOREACH(s, &named_sems, entry) {
168{
169 sem_t *sem;
170 sem_t s;
171 semid_t semid;
172 mode_t mode;
173 unsigned int value;
174
175 mode = 0;

--- 14 unchanged lines hidden (view full) ---

190 if (ksem_open(&semid, name, oflag, mode, value) == -1)
191 return (SEM_FAILED);
192 /*
193 * search for a duplicate ID, we must return the same sem_t *
194 * if we locate one.
195 */
196 _pthread_mutex_lock(&named_sems_mtx);
197 LIST_FOREACH(s, &named_sems, entry) {
251 if (s->semid == semid) {
252 sem = s->backpointer;
253 _pthread_mutex_unlock(&named_sems_mtx);
254 return (sem);
255 }
198 if (s->semid == semid) {
199 _pthread_mutex_unlock(&named_sems_mtx);
200 return (s->backpointer);
201 }
256 }
257 sem = (sem_t *)malloc(sizeof(*sem));
258 if (sem == NULL)
259 goto err;
260 *sem = sem_alloc(value, semid, 1);
261 if ((*sem) == NULL)
262 goto err;
202 }
203 sem = (sem_t *)malloc(sizeof(*sem));
204 if (sem == NULL)
205 goto err;
206 *sem = sem_alloc(value, semid, 1);
207 if ((*sem) == NULL)
208 goto err;
263 LIST_INSERT_HEAD(&named_sems, *sem, entry);
264 (*sem)->backpointer = sem;
265 _pthread_mutex_unlock(&named_sems_mtx);
209 _pthread_mutex_unlock(&named_sems_mtx);
210 (*sem)->backpointer = sem;
266 return (sem);
267err:
268 _pthread_mutex_unlock(&named_sems_mtx);
269 ksem_close(semid);
270 if (sem != NULL) {
271 if (*sem != NULL)
272 sem_free(*sem);
273 else
274 errno = ENOSPC;
275 free(sem);
276 } else {
277 errno = ENOSPC;
278 }
279 return (SEM_FAILED);
280}
281
282int
211 return (sem);
212err:
213 _pthread_mutex_unlock(&named_sems_mtx);
214 ksem_close(semid);
215 if (sem != NULL) {
216 if (*sem != NULL)
217 sem_free(*sem);
218 else
219 errno = ENOSPC;
220 free(sem);
221 } else {
222 errno = ENOSPC;
223 }
224 return (SEM_FAILED);
225}
226
227int
283_libc_sem_close_compat(sem_t *sem)
228sem_close(sem_t *sem)
284{
285
229{
230
286 if (sem_check_validity(sem) != 0)
287 return (-1);
288
289 if ((*sem)->syssem == 0) {
290 errno = EINVAL;
291 return (-1);
292 }
231 if ((*sem)->syssem == 0) {
232 errno = EINVAL;
233 return (-1);
234 }
293
294 _pthread_mutex_lock(&named_sems_mtx);
235 _pthread_mutex_lock(&named_sems_mtx);
295 if (ksem_close((*sem)->semid) != 0) {
236 if (ksem_close((*sem)->semid) == -1) {
296 _pthread_mutex_unlock(&named_sems_mtx);
297 return (-1);
298 }
299 LIST_REMOVE((*sem), entry);
300 _pthread_mutex_unlock(&named_sems_mtx);
301 sem_free(*sem);
237 _pthread_mutex_unlock(&named_sems_mtx);
238 return (-1);
239 }
240 LIST_REMOVE((*sem), entry);
241 _pthread_mutex_unlock(&named_sems_mtx);
242 sem_free(*sem);
302 *sem = NULL;
303 free(sem);
304 return (0);
305}
306
307int
243 free(sem);
244 return (0);
245}
246
247int
308_libc_sem_unlink_compat(const char *name)
248sem_unlink(const char *name)
309{
310
311 return (ksem_unlink(name));
312}
313
249{
250
251 return (ksem_unlink(name));
252}
253
314static int
315enable_async_cancel(void)
254int
255sem_wait(sem_t *sem)
316{
256{
317 int old;
257 int retval;
318
258
319 _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
320 return (old);
321}
259 _SEM_CHECK_VALIDITY(sem);
322
260
323static void
324restore_async_cancel(int val)
325{
326 _pthread_setcanceltype(val, NULL);
327}
328
329static int
330_umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *timeout)
331{
332 if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
333 timeout->tv_nsec <= 0))) {
334 errno = ETIMEDOUT;
335 return (-1);
261 if ((*sem)->syssem != 0) {
262 retval = ksem_wait((*sem)->semid);
263 goto RETURN;
336 }
264 }
337 return _umtx_op(__DEVOLATILE(void *, mtx),
338 UMTX_OP_WAIT_UINT_PRIVATE, id, NULL, __DECONST(void*, timeout));
339}
340
265
341static int
342_umtx_wake(volatile void *mtx)
343{
344 return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE,
345 1, NULL, NULL);
346}
266 _pthread_mutex_lock(&(*sem)->lock);
347
267
348#define TIMESPEC_SUB(dst, src, val) \
349 do { \
350 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \
351 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \
352 if ((dst)->tv_nsec < 0) { \
353 (dst)->tv_sec--; \
354 (dst)->tv_nsec += 1000000000; \
355 } \
356 } while (0)
268 while ((*sem)->count == 0) {
269 (*sem)->nwaiters++;
270 _pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock);
271 (*sem)->nwaiters--;
272 }
273 (*sem)->count--;
357
274
275 _pthread_mutex_unlock(&(*sem)->lock);
358
276
359static void
360sem_cancel_handler(void *arg)
361{
362 sem_t *sem = arg;
363
364 atomic_add_int(&(*sem)->nwaiters, -1);
365 if ((*sem)->nwaiters && (*sem)->count)
366 _umtx_wake(&(*sem)->count);
277 retval = 0;
278 RETURN:
279 return retval;
367}
368
369int
280}
281
282int
370_libc_sem_timedwait_compat(sem_t * __restrict sem,
371 const struct timespec * __restrict abstime)
283sem_trywait(sem_t *sem)
372{
284{
373 struct timespec ts, ts2;
374 int val, retval, saved_cancel;
285 int retval;
375
286
376 if (sem_check_validity(sem) != 0)
377 return (-1);
287 _SEM_CHECK_VALIDITY(sem);
378
379 if ((*sem)->syssem != 0) {
288
289 if ((*sem)->syssem != 0) {
380 saved_cancel = enable_async_cancel();
381 retval = ksem_wait((*sem)->semid);
382 restore_async_cancel(saved_cancel);
383 return (retval);
290 retval = ksem_trywait((*sem)->semid);
291 goto RETURN;
384 }
385
292 }
293
386 retval = 0;
387 _pthread_testcancel();
388 for (;;) {
389 while ((val = (*sem)->count) > 0) {
390 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
391 return (0);
392 }
393 if (retval)
394 break;
395 if (abstime) {
396 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
397 errno = EINVAL;
398 return (-1);
399 }
400 clock_gettime(CLOCK_REALTIME, &ts);
401 TIMESPEC_SUB(&ts2, abstime, &ts);
402 }
403 atomic_add_int(&(*sem)->nwaiters, 1);
404 pthread_cleanup_push(sem_cancel_handler, sem);
405 saved_cancel = enable_async_cancel();
406 retval = _umtx_wait_uint(&(*sem)->count, 0, abstime ? &ts2 : NULL);
407 restore_async_cancel(saved_cancel);
408 pthread_cleanup_pop(0);
409 atomic_add_int(&(*sem)->nwaiters, -1);
294 _pthread_mutex_lock(&(*sem)->lock);
295
296 if ((*sem)->count > 0) {
297 (*sem)->count--;
298 retval = 0;
299 } else {
300 errno = EAGAIN;
301 retval = -1;
410 }
302 }
411 return (retval);
412}
303
304 _pthread_mutex_unlock(&(*sem)->lock);
413
305
414int
415_libc_sem_wait_compat(sem_t *sem)
416{
417 return _libc_sem_timedwait_compat(sem, NULL);
306 RETURN:
307 return retval;
418}
419
420int
308}
309
310int
421_libc_sem_trywait_compat(sem_t *sem)
311sem_post(sem_t *sem)
422{
312{
423 int val;
313 int retval;
424
314
425 if (sem_check_validity(sem) != 0)
426 return (-1);
315 _SEM_CHECK_VALIDITY(sem);
427
316
428 if ((*sem)->syssem != 0)
429 return ksem_trywait((*sem)->semid);
430
431 while ((val = (*sem)->count) > 0) {
432 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
433 return (0);
317 if ((*sem)->syssem != 0) {
318 retval = ksem_post((*sem)->semid);
319 goto RETURN;
434 }
320 }
435 errno = EAGAIN;
436 return (-1);
437}
438
321
439int
440_libc_sem_post_compat(sem_t *sem)
441{
322 _pthread_mutex_lock(&(*sem)->lock);
442
323
443 if (sem_check_validity(sem) != 0)
444 return (-1);
324 (*sem)->count++;
325 if ((*sem)->nwaiters > 0)
326 _pthread_cond_signal(&(*sem)->gtzero);
445
327
446 if ((*sem)->syssem != 0)
447 return ksem_post((*sem)->semid);
328 _pthread_mutex_unlock(&(*sem)->lock);
448
329
449 atomic_add_rel_int(&(*sem)->count, 1);
450
451 if ((*sem)->nwaiters)
452 return _umtx_wake(&(*sem)->count);
453 return (0);
330 retval = 0;
331 RETURN:
332 return retval;
454}
455
456int
333}
334
335int
457_libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval)
336sem_getvalue(sem_t *sem, int *sval)
458{
337{
459 int retval;
338 int retval;
460
339
461 if (sem_check_validity(sem) != 0)
462 return (-1);
340 _SEM_CHECK_VALIDITY(sem);
463
341
464 if ((*sem)->syssem != 0)
342 if ((*sem)->syssem != 0) {
465 retval = ksem_getvalue((*sem)->semid, sval);
343 retval = ksem_getvalue((*sem)->semid, sval);
466 else {
467 *sval = (int)(*sem)->count;
468 retval = 0;
344 goto RETURN;
469 }
345 }
470 return (retval);
346
347 _pthread_mutex_lock(&(*sem)->lock);
348 *sval = (int)(*sem)->count;
349 _pthread_mutex_unlock(&(*sem)->lock);
350
351 retval = 0;
352 RETURN:
353 return retval;
471}
354}