1bf4dc877SAlfred Perlstein /* 2bf4dc877SAlfred Perlstein * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. 3bf4dc877SAlfred Perlstein * All rights reserved. 4bf4dc877SAlfred Perlstein * 5bf4dc877SAlfred Perlstein * Redistribution and use in source and binary forms, with or without 6bf4dc877SAlfred Perlstein * modification, are permitted provided that the following conditions 7bf4dc877SAlfred Perlstein * are met: 8bf4dc877SAlfred Perlstein * 1. Redistributions of source code must retain the above copyright 9bf4dc877SAlfred Perlstein * notice(s), this list of conditions and the following disclaimer as 10bf4dc877SAlfred Perlstein * the first lines of this file unmodified other than the possible 11bf4dc877SAlfred Perlstein * addition of one or more copyright notices. 12bf4dc877SAlfred Perlstein * 2. Redistributions in binary form must reproduce the above copyright 13bf4dc877SAlfred Perlstein * notice(s), this list of conditions and the following disclaimer in 14bf4dc877SAlfred Perlstein * the documentation and/or other materials provided with the 15bf4dc877SAlfred Perlstein * distribution. 16bf4dc877SAlfred Perlstein * 17bf4dc877SAlfred Perlstein * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18bf4dc877SAlfred Perlstein * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19bf4dc877SAlfred Perlstein * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20bf4dc877SAlfred Perlstein * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21bf4dc877SAlfred Perlstein * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22bf4dc877SAlfred Perlstein * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23bf4dc877SAlfred Perlstein * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24bf4dc877SAlfred Perlstein * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25bf4dc877SAlfred Perlstein * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26bf4dc877SAlfred Perlstein * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27bf4dc877SAlfred Perlstein * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28bf4dc877SAlfred Perlstein * 29bf4dc877SAlfred Perlstein * $FreeBSD$ 30bf4dc877SAlfred Perlstein */ 31bf4dc877SAlfred Perlstein 325c70dac8SDaniel Eischen /* 335c70dac8SDaniel Eischen * Some notes about this implementation. 345c70dac8SDaniel Eischen * 355c70dac8SDaniel Eischen * This is mostly a simple implementation of POSIX semaphores that 365c70dac8SDaniel Eischen * does not need threading. Any semaphore created is a kernel-based 375c70dac8SDaniel Eischen * semaphore regardless of the pshared attribute. This is necessary 385c70dac8SDaniel Eischen * because libc's stub for pthread_cond_wait() doesn't really wait, 395c70dac8SDaniel Eischen * and it is not worth the effort impose this behavior on libc. 405c70dac8SDaniel Eischen * 415c70dac8SDaniel Eischen * All functions here are designed to be thread-safe so that a 425c70dac8SDaniel Eischen * threads library need not provide wrappers except to make 435c70dac8SDaniel Eischen * sem_wait() and sem_timedwait() cancellation points or to 445c70dac8SDaniel Eischen * provide a faster userland implementation for non-pshared 455c70dac8SDaniel Eischen * semaphores. 465c70dac8SDaniel Eischen * 475c70dac8SDaniel Eischen * Also, this implementation of semaphores cannot really support 485c70dac8SDaniel Eischen * real pshared semaphores. The sem_t is an allocated object 495c70dac8SDaniel Eischen * and can't be seen by other processes when placed in shared 505c70dac8SDaniel Eischen * memory. It should work across forks as long as the semaphore 515c70dac8SDaniel Eischen * is created before any forks. 525c70dac8SDaniel Eischen * 535c70dac8SDaniel Eischen * The function sem_init() should be overridden by a threads 545c70dac8SDaniel Eischen * library if it wants to provide a different userland version 555c70dac8SDaniel Eischen * of semaphores. The functions sem_wait() and sem_timedwait() 565c70dac8SDaniel Eischen * need to be wrapped to provide cancellation points. The function 575c70dac8SDaniel Eischen * sem_post() may need to be wrapped to be signal-safe. 585c70dac8SDaniel Eischen */ 59e0554a53SJacques Vidrine #include "namespace.h" 605c70dac8SDaniel Eischen #include <sys/queue.h> 61bf4dc877SAlfred Perlstein #include <errno.h> 62bf4dc877SAlfred Perlstein #include <fcntl.h> 635c70dac8SDaniel Eischen #include <pthread.h> 64bf4dc877SAlfred Perlstein #include <semaphore.h> 65bf4dc877SAlfred Perlstein #include <stdarg.h> 665c70dac8SDaniel Eischen #include <stdlib.h> 675c70dac8SDaniel Eischen #include <time.h> 68bf4dc877SAlfred Perlstein #include <_semaphore.h> 69e0554a53SJacques Vidrine #include "un-namespace.h" 705c70dac8SDaniel Eischen #include "libc_private.h" 71bf4dc877SAlfred Perlstein 72bf4dc877SAlfred Perlstein static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); 73bf4dc877SAlfred Perlstein static void sem_free(sem_t sem); 74bf4dc877SAlfred Perlstein 75bf4dc877SAlfred Perlstein static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems); 76bf4dc877SAlfred Perlstein static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; 77bf4dc877SAlfred Perlstein 785c70dac8SDaniel Eischen __weak_reference(__sem_init, sem_init); 795c70dac8SDaniel Eischen __weak_reference(__sem_destroy, sem_destroy); 805c70dac8SDaniel Eischen __weak_reference(__sem_open, sem_open); 815c70dac8SDaniel Eischen __weak_reference(__sem_close, sem_close); 825c70dac8SDaniel Eischen __weak_reference(__sem_unlink, sem_post); 835c70dac8SDaniel Eischen __weak_reference(__sem_wait, sem_wait); 845c70dac8SDaniel Eischen __weak_reference(__sem_trywait, sem_trywait); 855c70dac8SDaniel Eischen __weak_reference(__sem_timedwait, sem_timedwait); 865c70dac8SDaniel Eischen __weak_reference(__sem_post, sem_post); 875c70dac8SDaniel Eischen __weak_reference(__sem_getvalue, sem_getvalue); 885c70dac8SDaniel Eischen 895c70dac8SDaniel Eischen 905c70dac8SDaniel Eischen static inline int 915c70dac8SDaniel Eischen sem_check_validity(sem_t *sem) 925c70dac8SDaniel Eischen { 935c70dac8SDaniel Eischen 945c70dac8SDaniel Eischen if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 955c70dac8SDaniel Eischen return (0); 965c70dac8SDaniel Eischen else { 975c70dac8SDaniel Eischen errno = EINVAL; 985c70dac8SDaniel Eischen return (-1); 995c70dac8SDaniel Eischen } 1005c70dac8SDaniel Eischen } 1015c70dac8SDaniel Eischen 102bf4dc877SAlfred Perlstein static void 103bf4dc877SAlfred Perlstein sem_free(sem_t sem) 104bf4dc877SAlfred Perlstein { 105bf4dc877SAlfred Perlstein 106bf4dc877SAlfred Perlstein _pthread_mutex_destroy(&sem->lock); 107bf4dc877SAlfred Perlstein _pthread_cond_destroy(&sem->gtzero); 108bf4dc877SAlfred Perlstein sem->magic = 0; 109bf4dc877SAlfred Perlstein free(sem); 110bf4dc877SAlfred Perlstein } 111bf4dc877SAlfred Perlstein 112bf4dc877SAlfred Perlstein static sem_t 113bf4dc877SAlfred Perlstein sem_alloc(unsigned int value, semid_t semid, int system_sem) 114bf4dc877SAlfred Perlstein { 115bf4dc877SAlfred Perlstein sem_t sem; 116bf4dc877SAlfred Perlstein 117bf4dc877SAlfred Perlstein if (value > SEM_VALUE_MAX) { 118bf4dc877SAlfred Perlstein errno = EINVAL; 119bf4dc877SAlfred Perlstein return (NULL); 120bf4dc877SAlfred Perlstein } 121bf4dc877SAlfred Perlstein 122bf4dc877SAlfred Perlstein sem = (sem_t)malloc(sizeof(struct sem)); 123bf4dc877SAlfred Perlstein if (sem == NULL) { 124bf4dc877SAlfred Perlstein errno = ENOSPC; 125bf4dc877SAlfred Perlstein return (NULL); 126bf4dc877SAlfred Perlstein } 127bf4dc877SAlfred Perlstein 128bf4dc877SAlfred Perlstein sem->count = (u_int32_t)value; 129bf4dc877SAlfred Perlstein sem->nwaiters = 0; 130bf4dc877SAlfred Perlstein sem->magic = SEM_MAGIC; 131bf4dc877SAlfred Perlstein sem->semid = semid; 132bf4dc877SAlfred Perlstein sem->syssem = system_sem; 1335c70dac8SDaniel Eischen sem->lock = PTHREAD_MUTEX_INITIALIZER; 1345c70dac8SDaniel Eischen sem->gtzero = PTHREAD_COND_INITIALIZER; 135bf4dc877SAlfred Perlstein return (sem); 136bf4dc877SAlfred Perlstein } 137bf4dc877SAlfred Perlstein 138bf4dc877SAlfred Perlstein int 1395c70dac8SDaniel Eischen __sem_init(sem_t *sem, int pshared, unsigned int value) 140bf4dc877SAlfred Perlstein { 141bf4dc877SAlfred Perlstein semid_t semid; 142bf4dc877SAlfred Perlstein 143bf4dc877SAlfred Perlstein /* 1445c70dac8SDaniel Eischen * We always have to create the kernel semaphore if the 1455c70dac8SDaniel Eischen * threads library isn't present since libc's version of 1465c70dac8SDaniel Eischen * pthread_cond_wait() is just a stub that doesn't really 1475c70dac8SDaniel Eischen * wait. 148bf4dc877SAlfred Perlstein */ 1495c70dac8SDaniel Eischen if (ksem_init(&semid, value) != 0) 1505c70dac8SDaniel Eischen return (-1); 151bf4dc877SAlfred Perlstein 1525c70dac8SDaniel Eischen (*sem) = sem_alloc(value, semid, 1); 1535c70dac8SDaniel Eischen if ((*sem) == NULL) { 154bf4dc877SAlfred Perlstein ksem_destroy(semid); 1555c70dac8SDaniel Eischen return (-1); 1565c70dac8SDaniel Eischen } 1575c70dac8SDaniel Eischen return (0); 158bf4dc877SAlfred Perlstein } 159bf4dc877SAlfred Perlstein 160bf4dc877SAlfred Perlstein int 1615c70dac8SDaniel Eischen __sem_destroy(sem_t *sem) 162bf4dc877SAlfred Perlstein { 163bf4dc877SAlfred Perlstein int retval; 164bf4dc877SAlfred Perlstein 1655c70dac8SDaniel Eischen if (sem_check_validity(sem) == 0) 1665c70dac8SDaniel Eischen return (-1); 167bf4dc877SAlfred Perlstein 168bf4dc877SAlfred Perlstein _pthread_mutex_lock(&(*sem)->lock); 169bf4dc877SAlfred Perlstein /* 170bf4dc877SAlfred Perlstein * If this is a system semaphore let the kernel track it otherwise 171bf4dc877SAlfred Perlstein * make sure there are no waiters. 172bf4dc877SAlfred Perlstein */ 1735c70dac8SDaniel Eischen if ((*sem)->syssem != 0) 174bf4dc877SAlfred Perlstein retval = ksem_destroy((*sem)->semid); 1755c70dac8SDaniel Eischen else if ((*sem)->nwaiters > 0) { 176bf4dc877SAlfred Perlstein errno = EBUSY; 177bf4dc877SAlfred Perlstein retval = -1; 1785c70dac8SDaniel Eischen } 1795c70dac8SDaniel Eischen else { 1805c70dac8SDaniel Eischen retval = 0; 1815c70dac8SDaniel Eischen (*sem)->magic = 0; 182bf4dc877SAlfred Perlstein } 183bf4dc877SAlfred Perlstein _pthread_mutex_unlock(&(*sem)->lock); 184bf4dc877SAlfred Perlstein 1855c70dac8SDaniel Eischen if (retval == 0) 186bf4dc877SAlfred Perlstein sem_free(*sem); 1875c70dac8SDaniel Eischen return (retval); 188bf4dc877SAlfred Perlstein } 189bf4dc877SAlfred Perlstein 190bf4dc877SAlfred Perlstein sem_t * 1915c70dac8SDaniel Eischen __sem_open(const char *name, int oflag, ...) 192bf4dc877SAlfred Perlstein { 193bf4dc877SAlfred Perlstein sem_t *sem; 194bf4dc877SAlfred Perlstein sem_t s; 195bf4dc877SAlfred Perlstein semid_t semid; 196bf4dc877SAlfred Perlstein mode_t mode; 197bf4dc877SAlfred Perlstein unsigned int value; 198bf4dc877SAlfred Perlstein 199bf4dc877SAlfred Perlstein mode = 0; 200bf4dc877SAlfred Perlstein value = 0; 201bf4dc877SAlfred Perlstein 202bf4dc877SAlfred Perlstein if ((oflag & O_CREAT) != 0) { 203bf4dc877SAlfred Perlstein va_list ap; 204bf4dc877SAlfred Perlstein 205bf4dc877SAlfred Perlstein va_start(ap, oflag); 206bf4dc877SAlfred Perlstein mode = va_arg(ap, int); 207bf4dc877SAlfred Perlstein value = va_arg(ap, unsigned int); 208bf4dc877SAlfred Perlstein va_end(ap); 209bf4dc877SAlfred Perlstein } 210bf4dc877SAlfred Perlstein /* 211bf4dc877SAlfred Perlstein * we can be lazy and let the kernel handle the "oflag", 212bf4dc877SAlfred Perlstein * we'll just merge duplicate IDs into our list. 213bf4dc877SAlfred Perlstein */ 214bf4dc877SAlfred Perlstein if (ksem_open(&semid, name, oflag, mode, value) == -1) 215bf4dc877SAlfred Perlstein return (SEM_FAILED); 216bf4dc877SAlfred Perlstein /* 217bf4dc877SAlfred Perlstein * search for a duplicate ID, we must return the same sem_t * 218bf4dc877SAlfred Perlstein * if we locate one. 219bf4dc877SAlfred Perlstein */ 220bf4dc877SAlfred Perlstein _pthread_mutex_lock(&named_sems_mtx); 221bf4dc877SAlfred Perlstein LIST_FOREACH(s, &named_sems, entry) { 222bf4dc877SAlfred Perlstein if (s->semid == semid) { 2235c70dac8SDaniel Eischen sem = s->backpointer; 224bf4dc877SAlfred Perlstein _pthread_mutex_unlock(&named_sems_mtx); 2255c70dac8SDaniel Eischen return (sem); 226bf4dc877SAlfred Perlstein } 227bf4dc877SAlfred Perlstein } 228bf4dc877SAlfred Perlstein sem = (sem_t *)malloc(sizeof(*sem)); 229bf4dc877SAlfred Perlstein if (sem == NULL) 230bf4dc877SAlfred Perlstein goto err; 231bf4dc877SAlfred Perlstein *sem = sem_alloc(value, semid, 1); 232bf4dc877SAlfred Perlstein if ((*sem) == NULL) 233bf4dc877SAlfred Perlstein goto err; 234a91b25dcSTim J. Robbins LIST_INSERT_HEAD(&named_sems, *sem, entry); 235bf4dc877SAlfred Perlstein (*sem)->backpointer = sem; 236a91b25dcSTim J. Robbins _pthread_mutex_unlock(&named_sems_mtx); 237bf4dc877SAlfred Perlstein return (sem); 238bf4dc877SAlfred Perlstein err: 239bf4dc877SAlfred Perlstein _pthread_mutex_unlock(&named_sems_mtx); 240bf4dc877SAlfred Perlstein ksem_close(semid); 241bf4dc877SAlfred Perlstein if (sem != NULL) { 242bf4dc877SAlfred Perlstein if (*sem != NULL) 243bf4dc877SAlfred Perlstein sem_free(*sem); 244bf4dc877SAlfred Perlstein else 245bf4dc877SAlfred Perlstein errno = ENOSPC; 246bf4dc877SAlfred Perlstein free(sem); 247bf4dc877SAlfred Perlstein } else { 248bf4dc877SAlfred Perlstein errno = ENOSPC; 249bf4dc877SAlfred Perlstein } 250bf4dc877SAlfred Perlstein return (SEM_FAILED); 251bf4dc877SAlfred Perlstein } 252bf4dc877SAlfred Perlstein 253bf4dc877SAlfred Perlstein int 2545c70dac8SDaniel Eischen __sem_close(sem_t *sem) 255bf4dc877SAlfred Perlstein { 256bf4dc877SAlfred Perlstein 2575c70dac8SDaniel Eischen if (sem_check_validity(sem) != 0) 2585c70dac8SDaniel Eischen return (-1); 2595c70dac8SDaniel Eischen 260bf4dc877SAlfred Perlstein if ((*sem)->syssem == 0) { 261bf4dc877SAlfred Perlstein errno = EINVAL; 262bf4dc877SAlfred Perlstein return (-1); 263bf4dc877SAlfred Perlstein } 2645c70dac8SDaniel Eischen 265bf4dc877SAlfred Perlstein _pthread_mutex_lock(&named_sems_mtx); 2665c70dac8SDaniel Eischen if (ksem_close((*sem)->semid) != 0) { 267bf4dc877SAlfred Perlstein _pthread_mutex_unlock(&named_sems_mtx); 268bf4dc877SAlfred Perlstein return (-1); 269bf4dc877SAlfred Perlstein } 270bf4dc877SAlfred Perlstein LIST_REMOVE((*sem), entry); 271bf4dc877SAlfred Perlstein _pthread_mutex_unlock(&named_sems_mtx); 272bf4dc877SAlfred Perlstein sem_free(*sem); 2735c70dac8SDaniel Eischen *sem = NULL; 274bf4dc877SAlfred Perlstein free(sem); 275bf4dc877SAlfred Perlstein return (0); 276bf4dc877SAlfred Perlstein } 277bf4dc877SAlfred Perlstein 278bf4dc877SAlfred Perlstein int 2795c70dac8SDaniel Eischen __sem_unlink(const char *name) 280bf4dc877SAlfred Perlstein { 281bf4dc877SAlfred Perlstein 282bf4dc877SAlfred Perlstein return (ksem_unlink(name)); 283bf4dc877SAlfred Perlstein } 284bf4dc877SAlfred Perlstein 285b6897522SDavid Xu int 2865c70dac8SDaniel Eischen __sem_wait(sem_t *sem) 287bf4dc877SAlfred Perlstein { 288bf4dc877SAlfred Perlstein 2895c70dac8SDaniel Eischen if (sem_check_validity(sem) != 0) 2905c70dac8SDaniel Eischen return (-1); 291bf4dc877SAlfred Perlstein 2925c70dac8SDaniel Eischen return (ksem_wait((*sem)->semid)); 293bf4dc877SAlfred Perlstein } 294bf4dc877SAlfred Perlstein 295bf4dc877SAlfred Perlstein int 2965c70dac8SDaniel Eischen __sem_trywait(sem_t *sem) 297bf4dc877SAlfred Perlstein { 298bf4dc877SAlfred Perlstein 2995c70dac8SDaniel Eischen if (sem_check_validity(sem) != 0) 3005c70dac8SDaniel Eischen return (-1); 301bf4dc877SAlfred Perlstein 3025c70dac8SDaniel Eischen return (ksem_trywait((*sem)->semid)); 303bf4dc877SAlfred Perlstein } 304bf4dc877SAlfred Perlstein 305bf4dc877SAlfred Perlstein int 3065c70dac8SDaniel Eischen __sem_timedwait(sem_t * __restrict sem, 3075c70dac8SDaniel Eischen struct timespec * __restrict abs_timeout) 308bf4dc877SAlfred Perlstein { 3095c70dac8SDaniel Eischen if (sem_check_validity(sem) != 0) 3105c70dac8SDaniel Eischen return (-1); 311bf4dc877SAlfred Perlstein 3125c70dac8SDaniel Eischen return (ksem_timedwait((*sem)->semid, abs_timeout)); 313bf4dc877SAlfred Perlstein } 314bf4dc877SAlfred Perlstein 315bf4dc877SAlfred Perlstein int 3165c70dac8SDaniel Eischen __sem_post(sem_t *sem) 3175c70dac8SDaniel Eischen { 3185c70dac8SDaniel Eischen 3195c70dac8SDaniel Eischen if (sem_check_validity(sem) != 0) 3205c70dac8SDaniel Eischen return (-1); 3215c70dac8SDaniel Eischen 3225c70dac8SDaniel Eischen return (ksem_post((*sem)->semid)); 3235c70dac8SDaniel Eischen } 3245c70dac8SDaniel Eischen 3255c70dac8SDaniel Eischen int 3265c70dac8SDaniel Eischen __sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 327bf4dc877SAlfred Perlstein { 328bf4dc877SAlfred Perlstein int retval; 329bf4dc877SAlfred Perlstein 3305c70dac8SDaniel Eischen if (sem_check_validity(sem) != 0) 3315c70dac8SDaniel Eischen return (-1); 332bf4dc877SAlfred Perlstein 3335c70dac8SDaniel Eischen if ((*sem)->syssem != 0) 334bf4dc877SAlfred Perlstein retval = ksem_getvalue((*sem)->semid, sval); 3355c70dac8SDaniel Eischen else { 336bf4dc877SAlfred Perlstein _pthread_mutex_lock(&(*sem)->lock); 337bf4dc877SAlfred Perlstein *sval = (int)(*sem)->count; 338bf4dc877SAlfred Perlstein _pthread_mutex_unlock(&(*sem)->lock); 339bf4dc877SAlfred Perlstein 340bf4dc877SAlfred Perlstein retval = 0; 3415c70dac8SDaniel Eischen } 3425c70dac8SDaniel Eischen return (retval); 343bf4dc877SAlfred Perlstein } 344