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 _umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *abstime) 316 { 317 struct _umtx_time *tm_p, timeout; 318 size_t tm_size; 319 320 if (abstime == NULL) { 321 tm_p = NULL; 322 tm_size = 0; 323 } else { 324 timeout._clockid = CLOCK_REALTIME; 325 timeout._flags = UMTX_ABSTIME; 326 timeout._timeout = *abstime; 327 tm_p = &timeout; 328 tm_size = sizeof(timeout); 329 } 330 return _umtx_op(__DEVOLATILE(void *, mtx), 331 UMTX_OP_WAIT_UINT_PRIVATE, id, 332 (void *)tm_size, __DECONST(void*, tm_p)); 333 } 334 335 static int 336 _umtx_wake(volatile void *mtx) 337 { 338 return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE, 339 1, NULL, NULL); 340 } 341 342 #define TIMESPEC_SUB(dst, src, val) \ 343 do { \ 344 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 345 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 346 if ((dst)->tv_nsec < 0) { \ 347 (dst)->tv_sec--; \ 348 (dst)->tv_nsec += 1000000000; \ 349 } \ 350 } while (0) 351 352 353 static void 354 sem_cancel_handler(void *arg) 355 { 356 sem_t *sem = arg; 357 358 atomic_add_int(&(*sem)->nwaiters, -1); 359 if ((*sem)->nwaiters && (*sem)->count) 360 _umtx_wake(&(*sem)->count); 361 } 362 363 int 364 _libc_sem_timedwait_compat(sem_t * __restrict sem, 365 const struct timespec * __restrict abstime) 366 { 367 int val, retval; 368 369 if (sem_check_validity(sem) != 0) 370 return (-1); 371 372 if ((*sem)->syssem != 0) { 373 _pthread_cancel_enter(1); 374 retval = ksem_wait((*sem)->semid); /* XXX no timeout */ 375 _pthread_cancel_leave(retval == -1); 376 return (retval); 377 } 378 379 retval = 0; 380 _pthread_testcancel(); 381 for (;;) { 382 while ((val = (*sem)->count) > 0) { 383 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 384 return (0); 385 } 386 if (retval) { 387 _pthread_testcancel(); 388 break; 389 } 390 if (abstime) { 391 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 392 errno = EINVAL; 393 return (-1); 394 } 395 } 396 atomic_add_int(&(*sem)->nwaiters, 1); 397 pthread_cleanup_push(sem_cancel_handler, sem); 398 _pthread_cancel_enter(1); 399 retval = _umtx_wait_uint(&(*sem)->count, 0, abstime); 400 _pthread_cancel_leave(0); 401 pthread_cleanup_pop(0); 402 atomic_add_int(&(*sem)->nwaiters, -1); 403 } 404 return (retval); 405 } 406 407 int 408 _libc_sem_wait_compat(sem_t *sem) 409 { 410 return _libc_sem_timedwait_compat(sem, NULL); 411 } 412 413 int 414 _libc_sem_trywait_compat(sem_t *sem) 415 { 416 int val; 417 418 if (sem_check_validity(sem) != 0) 419 return (-1); 420 421 if ((*sem)->syssem != 0) 422 return ksem_trywait((*sem)->semid); 423 424 while ((val = (*sem)->count) > 0) { 425 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 426 return (0); 427 } 428 errno = EAGAIN; 429 return (-1); 430 } 431 432 int 433 _libc_sem_post_compat(sem_t *sem) 434 { 435 436 if (sem_check_validity(sem) != 0) 437 return (-1); 438 439 if ((*sem)->syssem != 0) 440 return ksem_post((*sem)->semid); 441 442 atomic_add_rel_int(&(*sem)->count, 1); 443 rmb(); 444 if ((*sem)->nwaiters) 445 return _umtx_wake(&(*sem)->count); 446 return (0); 447 } 448 449 int 450 _libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval) 451 { 452 int retval; 453 454 if (sem_check_validity(sem) != 0) 455 return (-1); 456 457 if ((*sem)->syssem != 0) 458 retval = ksem_getvalue((*sem)->semid, sval); 459 else { 460 *sval = (int)(*sem)->count; 461 retval = 0; 462 } 463 return (retval); 464 } 465