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 11 * the first lines of this file unmodified other than the possible 12 * addition of one or more copyright notices. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice(s), this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 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 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> 64 #include <errno.h> 65 #include <sys/umtx.h> 66 #include <sys/_semaphore.h> 67 #include <limits.h> 68 #include <fcntl.h> 69 #include <pthread.h> 70 #include <stdarg.h> 71 #include <stdlib.h> 72 #include <time.h> 73 #include "un-namespace.h" 74 #include "libc_private.h" 75 76 /* 77 * Old semaphore definitions. 78 */ 79 struct 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 }; 92 93 typedef 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 106 static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); 107 static void sem_free(sem_t sem); 108 109 static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems); 110 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; 111 112 FB10_COMPAT(_libc_sem_init_compat, sem_init); 113 FB10_COMPAT(_libc_sem_destroy_compat, sem_destroy); 114 FB10_COMPAT(_libc_sem_open_compat, sem_open); 115 FB10_COMPAT(_libc_sem_close_compat, sem_close); 116 FB10_COMPAT(_libc_sem_unlink_compat, sem_unlink); 117 FB10_COMPAT(_libc_sem_wait_compat, sem_wait); 118 FB10_COMPAT(_libc_sem_trywait_compat, sem_trywait); 119 FB10_COMPAT(_libc_sem_timedwait_compat, sem_timedwait); 120 FB10_COMPAT(_libc_sem_post_compat, sem_post); 121 FB10_COMPAT(_libc_sem_getvalue_compat, sem_getvalue); 122 123 static inline int 124 sem_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 135 static void 136 sem_free(sem_t sem) 137 { 138 139 sem->magic = 0; 140 free(sem); 141 } 142 143 static sem_t 144 sem_alloc(unsigned int value, semid_t semid, int system_sem) 145 { 146 sem_t sem; 147 148 if (value > SEM_VALUE_MAX) { 149 errno = EINVAL; 150 return (NULL); 151 } 152 153 sem = (sem_t)malloc(sizeof(struct sem)); 154 if (sem == NULL) { 155 errno = ENOSPC; 156 return (NULL); 157 } 158 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 167 int 168 _libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value) 169 { 170 semid_t semid; 171 172 /* 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. 177 */ 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); 187 } 188 return (0); 189 } 190 191 int 192 _libc_sem_destroy_compat(sem_t *sem) 193 { 194 int retval; 195 196 if (sem_check_validity(sem) != 0) 197 return (-1); 198 199 /* 200 * If this is a system semaphore let the kernel track it otherwise 201 * make sure there are no waiters. 202 */ 203 if ((*sem)->syssem != 0) 204 retval = ksem_destroy((*sem)->semid); 205 else if ((*sem)->nwaiters > 0) { 206 errno = EBUSY; 207 retval = -1; 208 } 209 else { 210 retval = 0; 211 (*sem)->magic = 0; 212 } 213 214 if (retval == 0) 215 sem_free(*sem); 216 return (retval); 217 } 218 219 sem_t * 220 _libc_sem_open_compat(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; 229 value = 0; 230 231 if ((oflag & O_CREAT) != 0) { 232 va_list ap; 233 234 va_start(ap, oflag); 235 mode = va_arg(ap, int); 236 value = va_arg(ap, unsigned int); 237 va_end(ap); 238 } 239 /* 240 * we can be lazy and let the kernel handle the "oflag", 241 * we'll just merge duplicate IDs into our list. 242 */ 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) { 251 if (s->semid == semid) { 252 sem = s->backpointer; 253 _pthread_mutex_unlock(&named_sems_mtx); 254 return (sem); 255 } 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; 263 LIST_INSERT_HEAD(&named_sems, *sem, entry); 264 (*sem)->backpointer = sem; 265 _pthread_mutex_unlock(&named_sems_mtx); 266 return (sem); 267 err: 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 282 int 283 _libc_sem_close_compat(sem_t *sem) 284 { 285 286 if (sem_check_validity(sem) != 0) 287 return (-1); 288 289 if ((*sem)->syssem == 0) { 290 errno = EINVAL; 291 return (-1); 292 } 293 294 _pthread_mutex_lock(&named_sems_mtx); 295 if (ksem_close((*sem)->semid) != 0) { 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); 302 *sem = NULL; 303 free(sem); 304 return (0); 305 } 306 307 int 308 _libc_sem_unlink_compat(const char *name) 309 { 310 311 return (ksem_unlink(name)); 312 } 313 314 static int 315 enable_async_cancel(void) 316 { 317 int old; 318 319 _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); 320 return (old); 321 } 322 323 static void 324 restore_async_cancel(int val) 325 { 326 _pthread_setcanceltype(val, NULL); 327 } 328 329 static 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); 336 } 337 return _umtx_op(__DEVOLATILE(void *, mtx), 338 UMTX_OP_WAIT_UINT_PRIVATE, id, NULL, __DECONST(void*, timeout)); 339 } 340 341 static int 342 _umtx_wake(volatile void *mtx) 343 { 344 return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE, 345 1, NULL, NULL); 346 } 347 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) 357 358 359 static void 360 sem_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); 367 } 368 369 int 370 _libc_sem_timedwait_compat(sem_t * __restrict sem, 371 const struct timespec * __restrict abstime) 372 { 373 struct timespec ts, ts2; 374 int val, retval, saved_cancel; 375 376 if (sem_check_validity(sem) != 0) 377 return (-1); 378 379 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); 384 } 385 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); 410 } 411 return (retval); 412 } 413 414 int 415 _libc_sem_wait_compat(sem_t *sem) 416 { 417 return _libc_sem_timedwait_compat(sem, NULL); 418 } 419 420 int 421 _libc_sem_trywait_compat(sem_t *sem) 422 { 423 int val; 424 425 if (sem_check_validity(sem) != 0) 426 return (-1); 427 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); 434 } 435 errno = EAGAIN; 436 return (-1); 437 } 438 439 int 440 _libc_sem_post_compat(sem_t *sem) 441 { 442 443 if (sem_check_validity(sem) != 0) 444 return (-1); 445 446 if ((*sem)->syssem != 0) 447 return ksem_post((*sem)->semid); 448 449 atomic_add_rel_int(&(*sem)->count, 1); 450 451 if ((*sem)->nwaiters) 452 return _umtx_wake(&(*sem)->count); 453 return (0); 454 } 455 456 int 457 _libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval) 458 { 459 int retval; 460 461 if (sem_check_validity(sem) != 0) 462 return (-1); 463 464 if ((*sem)->syssem != 0) 465 retval = ksem_getvalue((*sem)->semid, sval); 466 else { 467 *sval = (int)(*sem)->count; 468 retval = 0; 469 } 470 return (retval); 471 } 472