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} |