1103578Salfred/* 2201546Sdavidxu * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3103578Salfred * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. 4103578Salfred * All rights reserved. 5103578Salfred * 6103578Salfred * Redistribution and use in source and binary forms, with or without 7103578Salfred * modification, are permitted provided that the following conditions 8103578Salfred * are met: 9103578Salfred * 1. Redistributions of source code must retain the above copyright 10103578Salfred * notice(s), this list of conditions and the following disclaimer as 11103578Salfred * the first lines of this file unmodified other than the possible 12103578Salfred * addition of one or more copyright notices. 13103578Salfred * 2. Redistributions in binary form must reproduce the above copyright 14103578Salfred * notice(s), this list of conditions and the following disclaimer in 15103578Salfred * the documentation and/or other materials provided with the 16103578Salfred * distribution. 17103578Salfred * 18103578Salfred * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 19103578Salfred * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20103578Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21103578Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 22103578Salfred * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23103578Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24103578Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25103578Salfred * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26103578Salfred * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27103578Salfred * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28103578Salfred * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29103578Salfred * 30103578Salfred * $FreeBSD$ 31103578Salfred */ 32103578Salfred 33125372Sdeischen/* 34125372Sdeischen * Some notes about this implementation. 35125372Sdeischen * 36125372Sdeischen * This is mostly a simple implementation of POSIX semaphores that 37125372Sdeischen * does not need threading. Any semaphore created is a kernel-based 38125372Sdeischen * semaphore regardless of the pshared attribute. This is necessary 39125372Sdeischen * because libc's stub for pthread_cond_wait() doesn't really wait, 40125372Sdeischen * and it is not worth the effort impose this behavior on libc. 41125372Sdeischen * 42125372Sdeischen * All functions here are designed to be thread-safe so that a 43125372Sdeischen * threads library need not provide wrappers except to make 44125372Sdeischen * sem_wait() and sem_timedwait() cancellation points or to 45125372Sdeischen * provide a faster userland implementation for non-pshared 46125372Sdeischen * semaphores. 47125372Sdeischen * 48125372Sdeischen * Also, this implementation of semaphores cannot really support 49125372Sdeischen * real pshared semaphores. The sem_t is an allocated object 50125372Sdeischen * and can't be seen by other processes when placed in shared 51125372Sdeischen * memory. It should work across forks as long as the semaphore 52125372Sdeischen * is created before any forks. 53125372Sdeischen * 54125372Sdeischen * The function sem_init() should be overridden by a threads 55125372Sdeischen * library if it wants to provide a different userland version 56125372Sdeischen * of semaphores. The functions sem_wait() and sem_timedwait() 57125372Sdeischen * need to be wrapped to provide cancellation points. The function 58125372Sdeischen * sem_post() may need to be wrapped to be signal-safe. 59125372Sdeischen */ 60111010Snectar#include "namespace.h" 61149313Sstefanf#include <sys/types.h> 62125372Sdeischen#include <sys/queue.h> 63201546Sdavidxu#include <machine/atomic.h> 64103578Salfred#include <errno.h> 65201546Sdavidxu#include <sys/umtx.h> 66201546Sdavidxu#include <sys/_semaphore.h> 67201546Sdavidxu#include <limits.h> 68103578Salfred#include <fcntl.h> 69125372Sdeischen#include <pthread.h> 70103578Salfred#include <stdarg.h> 71125372Sdeischen#include <stdlib.h> 72125372Sdeischen#include <time.h> 73111010Snectar#include "un-namespace.h" 74125372Sdeischen#include "libc_private.h" 75103578Salfred 76201546Sdavidxu/* 77201546Sdavidxu * Old semaphore definitions. 78201546Sdavidxu */ 79201546Sdavidxustruct sem { 80201546Sdavidxu#define SEM_MAGIC ((u_int32_t) 0x09fa4012) 81201546Sdavidxu u_int32_t magic; 82201546Sdavidxu pthread_mutex_t lock; 83201546Sdavidxu pthread_cond_t gtzero; 84201546Sdavidxu u_int32_t count; 85201546Sdavidxu u_int32_t nwaiters; 86201546Sdavidxu#define SEM_USER (NULL) 87201546Sdavidxu semid_t semid; /* semaphore id if kernel (shared) semaphore */ 88201546Sdavidxu int syssem; /* 1 if kernel (shared) semaphore */ 89201546Sdavidxu LIST_ENTRY(sem) entry; 90201546Sdavidxu struct sem **backpointer; 91201546Sdavidxu}; 92201546Sdavidxu 93201546Sdavidxutypedef struct sem* sem_t; 94201546Sdavidxu 95201546Sdavidxu#define SEM_FAILED ((sem_t *)0) 96201546Sdavidxu#define SEM_VALUE_MAX __INT_MAX 97201546Sdavidxu 98201546Sdavidxu#define SYM_FB10(sym) __CONCAT(sym, _fb10) 99201546Sdavidxu#define WEAK_REF(sym, alias) __weak_reference(sym, alias) 100201546Sdavidxu#define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver) 101201546Sdavidxu 102201546Sdavidxu#define FB10_COMPAT(func, sym) \ 103201546Sdavidxu WEAK_REF(func, SYM_FB10(sym)); \ 104201546Sdavidxu SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0) 105201546Sdavidxu 106103578Salfredstatic sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); 107125372Sdeischenstatic void sem_free(sem_t sem); 108103578Salfred 109202883Santoinestatic LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems); 110103578Salfredstatic pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; 111103578Salfred 112201546SdavidxuFB10_COMPAT(_libc_sem_init_compat, sem_init); 113201546SdavidxuFB10_COMPAT(_libc_sem_destroy_compat, sem_destroy); 114201546SdavidxuFB10_COMPAT(_libc_sem_open_compat, sem_open); 115201546SdavidxuFB10_COMPAT(_libc_sem_close_compat, sem_close); 116201546SdavidxuFB10_COMPAT(_libc_sem_unlink_compat, sem_unlink); 117201546SdavidxuFB10_COMPAT(_libc_sem_wait_compat, sem_wait); 118201546SdavidxuFB10_COMPAT(_libc_sem_trywait_compat, sem_trywait); 119201546SdavidxuFB10_COMPAT(_libc_sem_timedwait_compat, sem_timedwait); 120201546SdavidxuFB10_COMPAT(_libc_sem_post_compat, sem_post); 121201546SdavidxuFB10_COMPAT(_libc_sem_getvalue_compat, sem_getvalue); 122125372Sdeischen 123125372Sdeischenstatic inline int 124125372Sdeischensem_check_validity(sem_t *sem) 125125372Sdeischen{ 126125372Sdeischen 127125372Sdeischen if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 128125372Sdeischen return (0); 129125372Sdeischen else { 130125372Sdeischen errno = EINVAL; 131125372Sdeischen return (-1); 132125372Sdeischen } 133125372Sdeischen} 134125372Sdeischen 135103578Salfredstatic void 136103578Salfredsem_free(sem_t sem) 137103578Salfred{ 138103578Salfred 139103578Salfred sem->magic = 0; 140103578Salfred free(sem); 141103578Salfred} 142103578Salfred 143103578Salfredstatic sem_t 144103578Salfredsem_alloc(unsigned int value, semid_t semid, int system_sem) 145103578Salfred{ 146103578Salfred sem_t sem; 147103578Salfred 148103578Salfred if (value > SEM_VALUE_MAX) { 149103578Salfred errno = EINVAL; 150103578Salfred return (NULL); 151103578Salfred } 152103578Salfred 153103578Salfred sem = (sem_t)malloc(sizeof(struct sem)); 154103578Salfred if (sem == NULL) { 155103578Salfred errno = ENOSPC; 156103578Salfred return (NULL); 157103578Salfred } 158103578Salfred 159103578Salfred sem->count = (u_int32_t)value; 160103578Salfred sem->nwaiters = 0; 161103578Salfred sem->magic = SEM_MAGIC; 162103578Salfred sem->semid = semid; 163103578Salfred sem->syssem = system_sem; 164103578Salfred return (sem); 165103578Salfred} 166103578Salfred 167103578Salfredint 168201546Sdavidxu_libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value) 169103578Salfred{ 170125372Sdeischen semid_t semid; 171103578Salfred 172103578Salfred /* 173125372Sdeischen * We always have to create the kernel semaphore if the 174125372Sdeischen * threads library isn't present since libc's version of 175125372Sdeischen * pthread_cond_wait() is just a stub that doesn't really 176125372Sdeischen * wait. 177103578Salfred */ 178201546Sdavidxu semid = (semid_t)SEM_USER; 179201546Sdavidxu if ((pshared != 0) && ksem_init(&semid, value) != 0) 180125372Sdeischen return (-1); 181103578Salfred 182201546Sdavidxu *sem = sem_alloc(value, semid, pshared); 183125372Sdeischen if ((*sem) == NULL) { 184201546Sdavidxu if (pshared != 0) 185201546Sdavidxu ksem_destroy(semid); 186125372Sdeischen return (-1); 187125372Sdeischen } 188125372Sdeischen return (0); 189103578Salfred} 190103578Salfred 191103578Salfredint 192201546Sdavidxu_libc_sem_destroy_compat(sem_t *sem) 193103578Salfred{ 194125372Sdeischen int retval; 195103578Salfred 196125509Sdeischen if (sem_check_validity(sem) != 0) 197125372Sdeischen return (-1); 198125372Sdeischen 199103578Salfred /* 200103578Salfred * If this is a system semaphore let the kernel track it otherwise 201103578Salfred * make sure there are no waiters. 202103578Salfred */ 203125372Sdeischen if ((*sem)->syssem != 0) 204103578Salfred retval = ksem_destroy((*sem)->semid); 205125372Sdeischen else if ((*sem)->nwaiters > 0) { 206103578Salfred errno = EBUSY; 207103578Salfred retval = -1; 208103578Salfred } 209125372Sdeischen else { 210125372Sdeischen retval = 0; 211125372Sdeischen (*sem)->magic = 0; 212125372Sdeischen } 213103578Salfred 214201546Sdavidxu if (retval == 0) 215125372Sdeischen sem_free(*sem); 216125372Sdeischen return (retval); 217103578Salfred} 218103578Salfred 219103578Salfredsem_t * 220201546Sdavidxu_libc_sem_open_compat(const char *name, int oflag, ...) 221103578Salfred{ 222103578Salfred sem_t *sem; 223103578Salfred sem_t s; 224103578Salfred semid_t semid; 225103578Salfred mode_t mode; 226103578Salfred unsigned int value; 227103578Salfred 228103578Salfred mode = 0; 229103578Salfred value = 0; 230103578Salfred 231103578Salfred if ((oflag & O_CREAT) != 0) { 232103578Salfred va_list ap; 233103578Salfred 234103578Salfred va_start(ap, oflag); 235103578Salfred mode = va_arg(ap, int); 236103578Salfred value = va_arg(ap, unsigned int); 237103578Salfred va_end(ap); 238103578Salfred } 239103578Salfred /* 240103578Salfred * we can be lazy and let the kernel handle the "oflag", 241103578Salfred * we'll just merge duplicate IDs into our list. 242103578Salfred */ 243103578Salfred if (ksem_open(&semid, name, oflag, mode, value) == -1) 244103578Salfred return (SEM_FAILED); 245103578Salfred /* 246103578Salfred * search for a duplicate ID, we must return the same sem_t * 247103578Salfred * if we locate one. 248103578Salfred */ 249103578Salfred _pthread_mutex_lock(&named_sems_mtx); 250103578Salfred LIST_FOREACH(s, &named_sems, entry) { 251125372Sdeischen if (s->semid == semid) { 252125372Sdeischen sem = s->backpointer; 253125372Sdeischen _pthread_mutex_unlock(&named_sems_mtx); 254125372Sdeischen return (sem); 255125372Sdeischen } 256103578Salfred } 257103578Salfred sem = (sem_t *)malloc(sizeof(*sem)); 258103578Salfred if (sem == NULL) 259103578Salfred goto err; 260103578Salfred *sem = sem_alloc(value, semid, 1); 261103578Salfred if ((*sem) == NULL) 262103578Salfred goto err; 263109219Stjr LIST_INSERT_HEAD(&named_sems, *sem, entry); 264109219Stjr (*sem)->backpointer = sem; 265103578Salfred _pthread_mutex_unlock(&named_sems_mtx); 266103578Salfred return (sem); 267103578Salfrederr: 268103578Salfred _pthread_mutex_unlock(&named_sems_mtx); 269103578Salfred ksem_close(semid); 270103578Salfred if (sem != NULL) { 271103578Salfred if (*sem != NULL) 272103578Salfred sem_free(*sem); 273103578Salfred else 274103578Salfred errno = ENOSPC; 275103578Salfred free(sem); 276103578Salfred } else { 277103578Salfred errno = ENOSPC; 278103578Salfred } 279103578Salfred return (SEM_FAILED); 280103578Salfred} 281103578Salfred 282103578Salfredint 283201546Sdavidxu_libc_sem_close_compat(sem_t *sem) 284103578Salfred{ 285103578Salfred 286125372Sdeischen if (sem_check_validity(sem) != 0) 287125372Sdeischen return (-1); 288125372Sdeischen 289103578Salfred if ((*sem)->syssem == 0) { 290103578Salfred errno = EINVAL; 291103578Salfred return (-1); 292103578Salfred } 293125372Sdeischen 294103578Salfred _pthread_mutex_lock(&named_sems_mtx); 295125372Sdeischen if (ksem_close((*sem)->semid) != 0) { 296103578Salfred _pthread_mutex_unlock(&named_sems_mtx); 297103578Salfred return (-1); 298103578Salfred } 299103578Salfred LIST_REMOVE((*sem), entry); 300103578Salfred _pthread_mutex_unlock(&named_sems_mtx); 301103578Salfred sem_free(*sem); 302125372Sdeischen *sem = NULL; 303103578Salfred free(sem); 304103578Salfred return (0); 305103578Salfred} 306103578Salfred 307103578Salfredint 308201546Sdavidxu_libc_sem_unlink_compat(const char *name) 309103578Salfred{ 310103578Salfred 311103578Salfred return (ksem_unlink(name)); 312103578Salfred} 313103578Salfred 314201546Sdavidxustatic int 315201546Sdavidxu_umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *timeout) 316201546Sdavidxu{ 317201546Sdavidxu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 318201546Sdavidxu timeout->tv_nsec <= 0))) { 319201546Sdavidxu errno = ETIMEDOUT; 320125372Sdeischen return (-1); 321201546Sdavidxu } 322201546Sdavidxu return _umtx_op(__DEVOLATILE(void *, mtx), 323201546Sdavidxu UMTX_OP_WAIT_UINT_PRIVATE, id, NULL, __DECONST(void*, timeout)); 324201546Sdavidxu} 325103578Salfred 326201546Sdavidxustatic int 327201546Sdavidxu_umtx_wake(volatile void *mtx) 328201546Sdavidxu{ 329201546Sdavidxu return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE, 330201546Sdavidxu 1, NULL, NULL); 331125372Sdeischen} 332103578Salfred 333201546Sdavidxu#define TIMESPEC_SUB(dst, src, val) \ 334201546Sdavidxu do { \ 335201546Sdavidxu (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 336201546Sdavidxu (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 337201546Sdavidxu if ((dst)->tv_nsec < 0) { \ 338201546Sdavidxu (dst)->tv_sec--; \ 339201546Sdavidxu (dst)->tv_nsec += 1000000000; \ 340201546Sdavidxu } \ 341201546Sdavidxu } while (0) 342201546Sdavidxu 343201546Sdavidxu 344201546Sdavidxustatic void 345201546Sdavidxusem_cancel_handler(void *arg) 346201546Sdavidxu{ 347201546Sdavidxu sem_t *sem = arg; 348201546Sdavidxu 349201546Sdavidxu atomic_add_int(&(*sem)->nwaiters, -1); 350201546Sdavidxu if ((*sem)->nwaiters && (*sem)->count) 351201546Sdavidxu _umtx_wake(&(*sem)->count); 352201546Sdavidxu} 353201546Sdavidxu 354125372Sdeischenint 355201546Sdavidxu_libc_sem_timedwait_compat(sem_t * __restrict sem, 356201546Sdavidxu const struct timespec * __restrict abstime) 357125372Sdeischen{ 358201546Sdavidxu struct timespec ts, ts2; 359213153Sdavidxu int val, retval; 360103578Salfred 361125372Sdeischen if (sem_check_validity(sem) != 0) 362125372Sdeischen return (-1); 363103578Salfred 364201546Sdavidxu if ((*sem)->syssem != 0) { 365213153Sdavidxu _pthread_cancel_enter(1); 366213153Sdavidxu retval = ksem_wait((*sem)->semid); /* XXX no timeout */ 367213153Sdavidxu _pthread_cancel_leave(retval == -1); 368201546Sdavidxu return (retval); 369201546Sdavidxu } 370201546Sdavidxu 371201546Sdavidxu retval = 0; 372201546Sdavidxu _pthread_testcancel(); 373201546Sdavidxu for (;;) { 374201546Sdavidxu while ((val = (*sem)->count) > 0) { 375201546Sdavidxu if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 376201546Sdavidxu return (0); 377125446Sdeischen } 378213153Sdavidxu if (retval) { 379213153Sdavidxu _pthread_testcancel(); 380201546Sdavidxu break; 381213153Sdavidxu } 382201546Sdavidxu if (abstime) { 383201546Sdavidxu if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 384201546Sdavidxu errno = EINVAL; 385201546Sdavidxu return (-1); 386201546Sdavidxu } 387201546Sdavidxu clock_gettime(CLOCK_REALTIME, &ts); 388201546Sdavidxu TIMESPEC_SUB(&ts2, abstime, &ts); 389201546Sdavidxu } 390201546Sdavidxu atomic_add_int(&(*sem)->nwaiters, 1); 391201546Sdavidxu pthread_cleanup_push(sem_cancel_handler, sem); 392213153Sdavidxu _pthread_cancel_enter(1); 393201546Sdavidxu retval = _umtx_wait_uint(&(*sem)->count, 0, abstime ? &ts2 : NULL); 394213153Sdavidxu _pthread_cancel_leave(0); 395201546Sdavidxu pthread_cleanup_pop(0); 396201546Sdavidxu atomic_add_int(&(*sem)->nwaiters, -1); 397125446Sdeischen } 398125446Sdeischen return (retval); 399103578Salfred} 400103578Salfred 401103578Salfredint 402201546Sdavidxu_libc_sem_wait_compat(sem_t *sem) 403103578Salfred{ 404201546Sdavidxu return _libc_sem_timedwait_compat(sem, NULL); 405201546Sdavidxu} 406201546Sdavidxu 407201546Sdavidxuint 408201546Sdavidxu_libc_sem_trywait_compat(sem_t *sem) 409201546Sdavidxu{ 410201546Sdavidxu int val; 411201546Sdavidxu 412125372Sdeischen if (sem_check_validity(sem) != 0) 413125372Sdeischen return (-1); 414103578Salfred 415201546Sdavidxu if ((*sem)->syssem != 0) 416201546Sdavidxu return ksem_trywait((*sem)->semid); 417201546Sdavidxu 418201546Sdavidxu while ((val = (*sem)->count) > 0) { 419201546Sdavidxu if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 420201546Sdavidxu return (0); 421201546Sdavidxu } 422201546Sdavidxu errno = EAGAIN; 423201546Sdavidxu return (-1); 424103578Salfred} 425103578Salfred 426103578Salfredint 427201546Sdavidxu_libc_sem_post_compat(sem_t *sem) 428103578Salfred{ 429103578Salfred 430125372Sdeischen if (sem_check_validity(sem) != 0) 431125372Sdeischen return (-1); 432103578Salfred 433201546Sdavidxu if ((*sem)->syssem != 0) 434201546Sdavidxu return ksem_post((*sem)->semid); 435201546Sdavidxu 436201546Sdavidxu atomic_add_rel_int(&(*sem)->count, 1); 437201546Sdavidxu 438201546Sdavidxu if ((*sem)->nwaiters) 439201546Sdavidxu return _umtx_wake(&(*sem)->count); 440201546Sdavidxu return (0); 441103578Salfred} 442103578Salfred 443103578Salfredint 444201546Sdavidxu_libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval) 445103578Salfred{ 446125372Sdeischen int retval; 447103578Salfred 448125372Sdeischen if (sem_check_validity(sem) != 0) 449125372Sdeischen return (-1); 450103578Salfred 451125372Sdeischen if ((*sem)->syssem != 0) 452103578Salfred retval = ksem_getvalue((*sem)->semid, sval); 453125372Sdeischen else { 454125372Sdeischen *sval = (int)(*sem)->count; 455125372Sdeischen retval = 0; 456103578Salfred } 457125372Sdeischen return (retval); 458103578Salfred} 459