11897Swollman/* 21897Swollman * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 31897Swollman * All rights reserved. 41897Swollman * 51897Swollman * Redistribution and use in source and binary forms, with or without 61897Swollman * modification, are permitted provided that the following conditions 71897Swollman * are met: 812798Swpaul * 1. Redistributions of source code must retain the above copyright 91897Swollman * notice(s), this list of conditions and the following disclaimer as 101897Swollman * the first lines of this file unmodified other than the possible 111897Swollman * addition of one or more copyright notices. 1212798Swpaul * 2. Redistributions in binary form must reproduce the above copyright 131897Swollman * notice(s), this list of conditions and the following disclaimer in 141897Swollman * the documentation and/or other materials provided with the 151897Swollman * distribution. 1612798Swpaul * 171897Swollman * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 181897Swollman * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191897Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2012798Swpaul * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 211897Swollman * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 221897Swollman * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 231897Swollman * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 2412798Swpaul * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 251897Swollman * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 261897Swollman * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 271897Swollman * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 281897Swollman * 2912798Swpaul * $FreeBSD$ 3012798Swpaul */ 3112798Swpaul 321897Swollman#include "namespace.h" 3312798Swpaul#include <sys/types.h> 341897Swollman#include <sys/queue.h> 351897Swollman#include <sys/mman.h> 361897Swollman#include <sys/stat.h> 378874Srgrimes#include <errno.h> 388874Srgrimes#include <machine/atomic.h> 391897Swollman#include <sys/umtx.h> 401897Swollman#include <limits.h> 4112798Swpaul#include <fcntl.h> 4217142Sjkh#include <pthread.h> 4312798Swpaul#include <stdarg.h> 441897Swollman#include <stdlib.h> 451897Swollman#include <string.h> 4617142Sjkh#include <time.h> 4717142Sjkh#include <semaphore.h> 4817142Sjkh#include <unistd.h> 4917142Sjkh#include "un-namespace.h" 5017142Sjkh#include "libc_private.h" 5117142Sjkh 5217142Sjkh__weak_reference(_sem_close, sem_close); 5317142Sjkh__weak_reference(_sem_destroy, sem_destroy); 5417142Sjkh__weak_reference(_sem_getvalue, sem_getvalue); 5517142Sjkh__weak_reference(_sem_init, sem_init); 561897Swollman__weak_reference(_sem_open, sem_open); 571897Swollman__weak_reference(_sem_post, sem_post); 588874Srgrimes__weak_reference(_sem_timedwait, sem_timedwait); 591897Swollman__weak_reference(_sem_trywait, sem_trywait); 601897Swollman__weak_reference(_sem_unlink, sem_unlink); 611897Swollman__weak_reference(_sem_wait, sem_wait); 621897Swollman 631897Swollman#define SEM_PREFIX "/tmp/SEMD" 6412798Swpaul#define SEM_MAGIC ((u_int32_t)0x73656d31) 651897Swollman 661897Swollmanstruct sem_nameinfo { 6712798Swpaul int open_count; 6812798Swpaul char *name; 6912798Swpaul dev_t dev; 7012798Swpaul ino_t ino; 7112798Swpaul sem_t *sem; 7212798Swpaul LIST_ENTRY(sem_nameinfo) next; 7312798Swpaul}; 7412798Swpaul 7512798Swpaulstatic pthread_once_t once = PTHREAD_ONCE_INIT; 7612798Swpaulstatic pthread_mutex_t sem_llock; 7712798Swpaulstatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 7812798Swpaul 7912798Swpaulstatic void 8012798Swpaulsem_prefork() 811897Swollman{ 821897Swollman 831897Swollman _pthread_mutex_lock(&sem_llock); 841897Swollman} 851897Swollman 861897Swollmanstatic void 871897Swollmansem_postfork() 881897Swollman{ 891897Swollman _pthread_mutex_unlock(&sem_llock); 901897Swollman} 911897Swollman 921897Swollmanstatic void 931897Swollmansem_child_postfork() 941897Swollman{ 9517142Sjkh _pthread_mutex_unlock(&sem_llock); 9617142Sjkh} 971897Swollman 981897Swollmanstatic void 991897Swollmansem_module_init(void) 1001897Swollman{ 10117142Sjkh pthread_mutexattr_t ma; 1021897Swollman 1031897Swollman _pthread_mutexattr_init(&ma); 1041897Swollman _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 1051897Swollman _pthread_mutex_init(&sem_llock, &ma); 10612798Swpaul _pthread_mutexattr_destroy(&ma); 1071897Swollman _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 1081897Swollman} 1091897Swollman 1101897Swollmanstatic inline int 1111897Swollmansem_check_validity(sem_t *sem) 1121897Swollman{ 1131897Swollman 11417142Sjkh if (sem->_magic == SEM_MAGIC) 1151897Swollman return (0); 1161897Swollman else { 1171897Swollman errno = EINVAL; 1181897Swollman return (-1); 1191897Swollman } 1201897Swollman} 1211897Swollman 1221897Swollmanint 1231897Swollman_sem_init(sem_t *sem, int pshared, unsigned int value) 1241897Swollman{ 12517142Sjkh 12612798Swpaul if (value > SEM_VALUE_MAX) { 12712798Swpaul errno = EINVAL; 12812798Swpaul return (-1); 1291897Swollman } 13012798Swpaul 1311897Swollman bzero(sem, sizeof(sem_t)); 13212798Swpaul sem->_magic = SEM_MAGIC; 13312798Swpaul sem->_kern._count = (u_int32_t)value; 13412798Swpaul sem->_kern._has_waiters = 0; 13512798Swpaul sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 13612798Swpaul return (0); 13712798Swpaul} 13812798Swpaul 13912798Swpaulsem_t * 14012798Swpaul_sem_open(const char *name, int flags, ...) 14112798Swpaul{ 14212798Swpaul char path[PATH_MAX]; 14312798Swpaul 14412798Swpaul struct stat sb; 14512798Swpaul va_list ap; 1461897Swollman struct sem_nameinfo *ni = NULL; 1471897Swollman sem_t *sem = NULL; 1481897Swollman int fd = -1, mode, len, errsave; 14917142Sjkh int value = 0; 15012798Swpaul 15112798Swpaul if (name[0] != '/') { 15212798Swpaul errno = EINVAL; 15312798Swpaul return (SEM_FAILED); 15412798Swpaul } 15512798Swpaul name++; 15612798Swpaul strcpy(path, SEM_PREFIX); 15712798Swpaul if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 15812798Swpaul errno = ENAMETOOLONG; 15912798Swpaul return (SEM_FAILED); 16012798Swpaul } 16112798Swpaul if (flags & ~(O_CREAT|O_EXCL)) { 16212798Swpaul errno = EINVAL; 16312798Swpaul return (SEM_FAILED); 16412798Swpaul } 16517142Sjkh if ((flags & O_CREAT) != 0) { 16612798Swpaul va_start(ap, flags); 16712798Swpaul mode = va_arg(ap, int); 16812798Swpaul value = va_arg(ap, int); 16912798Swpaul va_end(ap); 17012798Swpaul } 17112798Swpaul fd = -1; 17217142Sjkh _pthread_once(&once, sem_module_init); 1731897Swollman 1741897Swollman _pthread_mutex_lock(&sem_llock); 1751897Swollman LIST_FOREACH(ni, &sem_list, next) { 1761897Swollman if (ni->name != NULL && strcmp(name, ni->name) == 0) { 1771897Swollman fd = _open(path, flags | O_RDWR | O_CLOEXEC | 1781897Swollman O_EXLOCK, mode); 1791897Swollman if (fd == -1 || _fstat(fd, &sb) == -1) 18017142Sjkh goto error; 1811897Swollman if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 1821897Swollman O_EXCL) || ni->dev != sb.st_dev || 1831897Swollman ni->ino != sb.st_ino) { 1841897Swollman ni->name = NULL; 1851897Swollman ni = NULL; 1861897Swollman break; 1871897Swollman } 1881897Swollman ni->open_count++; 18917142Sjkh sem = ni->sem; 1901897Swollman _pthread_mutex_unlock(&sem_llock); 1911897Swollman _close(fd); 1921897Swollman return (sem); 1931897Swollman } 1941897Swollman } 1951897Swollman 19617142Sjkh len = sizeof(*ni) + strlen(name) + 1; 19712798Swpaul ni = (struct sem_nameinfo *)malloc(len); 19812798Swpaul if (ni == NULL) { 1991897Swollman errno = ENOSPC; 2001897Swollman goto error; 2011897Swollman } 20212798Swpaul 20312798Swpaul ni->name = (char *)(ni+1); 20412798Swpaul strcpy(ni->name, name); 20512798Swpaul 20612798Swpaul if (fd == -1) { 20712798Swpaul fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 2081897Swollman if (fd == -1 || _fstat(fd, &sb) == -1) 20912798Swpaul goto error; 2101897Swollman } 21112798Swpaul if (sb.st_size < sizeof(sem_t)) { 2121897Swollman sem_t tmp; 2131897Swollman 2141897Swollman tmp._magic = SEM_MAGIC; 21512798Swpaul tmp._kern._has_waiters = 0; 2161897Swollman tmp._kern._count = value; 2171897Swollman tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 2181897Swollman if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 21917142Sjkh goto error; 2201897Swollman } 2211897Swollman flock(fd, LOCK_UN); 2221897Swollman sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 22312798Swpaul MAP_SHARED|MAP_NOSYNC, fd, 0); 2241897Swollman if (sem == MAP_FAILED) { 2251897Swollman sem = NULL; 2261897Swollman if (errno == ENOMEM) 2271897Swollman errno = ENOSPC; 22817142Sjkh goto error; 2291897Swollman } 2301897Swollman if (sem->_magic != SEM_MAGIC) { 2311897Swollman errno = EINVAL; 2321897Swollman goto error; 2331897Swollman } 2341897Swollman ni->open_count = 1; 2351897Swollman ni->sem = sem; 2361897Swollman ni->dev = sb.st_dev; 2371897Swollman ni->ino = sb.st_ino; 2381897Swollman LIST_INSERT_HEAD(&sem_list, ni, next); 2391897Swollman _close(fd); 2401897Swollman _pthread_mutex_unlock(&sem_llock); 2411897Swollman return (sem); 2421897Swollman 2431897Swollmanerror: 2441897Swollman errsave = errno; 24512798Swpaul if (fd != -1) 2461897Swollman _close(fd); 2471897Swollman if (sem != NULL) 2481897Swollman munmap(sem, sizeof(sem_t)); 2491897Swollman free(ni); 2501897Swollman _pthread_mutex_unlock(&sem_llock); 2511897Swollman errno = errsave; 2521897Swollman return (SEM_FAILED); 2531897Swollman} 2541897Swollman 2551897Swollmanint 2561897Swollman_sem_close(sem_t *sem) 2571897Swollman{ 2581897Swollman struct sem_nameinfo *ni; 2591897Swollman 2601897Swollman if (sem_check_validity(sem) != 0) 2611897Swollman return (-1); 2621897Swollman 26312798Swpaul if (!(sem->_kern._flags & SEM_NAMED)) { 2641897Swollman errno = EINVAL; 2651897Swollman return (-1); 2661897Swollman } 2671897Swollman 2681897Swollman _pthread_once(&once, sem_module_init); 2691897Swollman 2701897Swollman _pthread_mutex_lock(&sem_llock); 2711897Swollman LIST_FOREACH(ni, &sem_list, next) { 2721897Swollman if (sem == ni->sem) { 2731897Swollman if (--ni->open_count > 0) { 2741897Swollman _pthread_mutex_unlock(&sem_llock); 2751897Swollman return (0); 2761897Swollman } 2771897Swollman else 2781897Swollman break; 2791897Swollman } 2801897Swollman } 2811897Swollman 2821897Swollman if (ni) { 28312798Swpaul LIST_REMOVE(ni, next); 2841897Swollman _pthread_mutex_unlock(&sem_llock); 2851897Swollman munmap(sem, sizeof(*sem)); 28612798Swpaul free(ni); 28712798Swpaul return (0); 2881897Swollman } 2891897Swollman _pthread_mutex_unlock(&sem_llock); 2901897Swollman errno = EINVAL; 2911897Swollman return (-1); 2921897Swollman} 29312798Swpaul 2941897Swollmanint 2951897Swollman_sem_unlink(const char *name) 2961897Swollman{ 2971897Swollman char path[PATH_MAX]; 2981897Swollman 2991897Swollman if (name[0] != '/') { 3001897Swollman errno = ENOENT; 3011897Swollman return -1; 3021897Swollman } 3031897Swollman name++; 3041897Swollman strcpy(path, SEM_PREFIX); 30517142Sjkh if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 3061897Swollman errno = ENAMETOOLONG; 3071897Swollman return (-1); 3081897Swollman } 3091897Swollman 3101897Swollman return (unlink(path)); 3111897Swollman} 3121897Swollman 3131897Swollmanint 31417142Sjkh_sem_destroy(sem_t *sem) 31512798Swpaul{ 31612798Swpaul 31712798Swpaul if (sem_check_validity(sem) != 0) 31812798Swpaul return (-1); 31912798Swpaul 32012798Swpaul if (sem->_kern._flags & SEM_NAMED) { 3211897Swollman errno = EINVAL; 32212798Swpaul return (-1); 32312798Swpaul } 32412798Swpaul sem->_magic = 0; 32512798Swpaul return (0); 32612798Swpaul} 32712798Swpaul 32812798Swpaulint 32912798Swpaul_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 33012798Swpaul{ 33112798Swpaul 33212798Swpaul if (sem_check_validity(sem) != 0) 33312798Swpaul return (-1); 33412798Swpaul 33517142Sjkh *sval = (int)sem->_kern._count; 3361897Swollman return (0); 3371897Swollman} 3381897Swollman 3391897Swollmanstatic __inline int 3401897Swollmanusem_wake(struct _usem *sem) 3411897Swollman{ 3421897Swollman if (!sem->_has_waiters) 34312798Swpaul return (0); 3441897Swollman return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 3451897Swollman} 34612798Swpaul 3471897Swollmanstatic __inline int 3481897Swollmanusem_wait(struct _usem *sem, const struct timespec *timeout) 34912798Swpaul{ 35012798Swpaul if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 35112798Swpaul timeout->tv_nsec <= 0))) { 35212798Swpaul errno = ETIMEDOUT; 3531897Swollman return (-1); 3541897Swollman } 3551897Swollman return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, 3561897Swollman __DECONST(void*, timeout)); 35712798Swpaul} 35812798Swpaul 35912798Swpaulint 36012798Swpaul_sem_trywait(sem_t *sem) 36112798Swpaul{ 36212798Swpaul int val; 36312798Swpaul 36412798Swpaul if (sem_check_validity(sem) != 0) 36512798Swpaul return (-1); 3661897Swollman 3671897Swollman while ((val = sem->_kern._count) > 0) { 3681897Swollman if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 3691897Swollman return (0); 3701897Swollman } 3711897Swollman errno = EAGAIN; 3721897Swollman return (-1); 3731897Swollman} 3741897Swollman 37512798Swpaul#define TIMESPEC_SUB(dst, src, val) \ 37612798Swpaul do { \ 37712798Swpaul (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 37812798Swpaul (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 37912798Swpaul if ((dst)->tv_nsec < 0) { \ 38012798Swpaul (dst)->tv_sec--; \ 38112798Swpaul (dst)->tv_nsec += 1000000000; \ 38212798Swpaul } \ 38312798Swpaul } while (0) 3841897Swollman 38512798Swpaul 3861897Swollmanint 3871897Swollman_sem_timedwait(sem_t * __restrict sem, 38817142Sjkh const struct timespec * __restrict abstime) 38917142Sjkh{ 39017142Sjkh struct timespec ts, ts2; 3911897Swollman int val, retval; 3921897Swollman 3931897Swollman if (sem_check_validity(sem) != 0) 3941897Swollman return (-1); 3951897Swollman 39612798Swpaul retval = 0; 3971897Swollman _pthread_testcancel(); 3981897Swollman for (;;) { 3991897Swollman while ((val = sem->_kern._count) > 0) { 40012798Swpaul if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 40112798Swpaul return (0); 40212798Swpaul } 40312798Swpaul 40412798Swpaul if (retval) { 40512798Swpaul _pthread_testcancel(); 40612798Swpaul break; 40712798Swpaul } 40812798Swpaul 40912798Swpaul /* 41012798Swpaul * The timeout argument is only supposed to 41112798Swpaul * be checked if the thread would have blocked. 4121897Swollman */ 41312798Swpaul if (abstime != NULL) { 41412798Swpaul if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 41512798Swpaul errno = EINVAL; 41612798Swpaul return (-1); 4171897Swollman } 41812798Swpaul clock_gettime(CLOCK_REALTIME, &ts); 41912798Swpaul TIMESPEC_SUB(&ts2, abstime, &ts); 42012798Swpaul } 42112798Swpaul _pthread_cancel_enter(1); 42212798Swpaul retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); 42312798Swpaul _pthread_cancel_leave(0); 42412798Swpaul } 42512798Swpaul return (retval); 42612798Swpaul} 42712798Swpaul 42812798Swpaulint 42912798Swpaul_sem_wait(sem_t *sem) 43012798Swpaul{ 43112798Swpaul return _sem_timedwait(sem, NULL); 43212798Swpaul} 43312798Swpaul 43412798Swpaul/* 43512798Swpaul * POSIX: 43612798Swpaul * The sem_post() interface is reentrant with respect to signals and may be 43712798Swpaul * invoked from a signal-catching function. 43812798Swpaul * The implementation does not use lock, so it should be safe. 43912798Swpaul */ 44012798Swpaulint 44112798Swpaul_sem_post(sem_t *sem) 44212798Swpaul{ 44312798Swpaul 44412798Swpaul if (sem_check_validity(sem) != 0) 44512798Swpaul return (-1); 44612798Swpaul 44712798Swpaul atomic_add_rel_int(&sem->_kern._count, 1); 44812798Swpaul return usem_wake(&sem->_kern); 44912798Swpaul} 45012798Swpaul