1201546Sdavidxu/* 2201546Sdavidxu * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3201546Sdavidxu * All rights reserved. 4201546Sdavidxu * 5201546Sdavidxu * Redistribution and use in source and binary forms, with or without 6201546Sdavidxu * modification, are permitted provided that the following conditions 7201546Sdavidxu * are met: 8201546Sdavidxu * 1. Redistributions of source code must retain the above copyright 9201546Sdavidxu * notice(s), this list of conditions and the following disclaimer as 10201546Sdavidxu * the first lines of this file unmodified other than the possible 11201546Sdavidxu * addition of one or more copyright notices. 12201546Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright 13201546Sdavidxu * notice(s), this list of conditions and the following disclaimer in 14201546Sdavidxu * the documentation and/or other materials provided with the 15201546Sdavidxu * distribution. 16201546Sdavidxu * 17201546Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18201546Sdavidxu * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19201546Sdavidxu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20201546Sdavidxu * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21201546Sdavidxu * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22201546Sdavidxu * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23201546Sdavidxu * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24201546Sdavidxu * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25201546Sdavidxu * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26201546Sdavidxu * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27201546Sdavidxu * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28201546Sdavidxu * 29201546Sdavidxu * $FreeBSD$ 30201546Sdavidxu */ 31201546Sdavidxu 32201546Sdavidxu#include "namespace.h" 33201546Sdavidxu#include <sys/types.h> 34201546Sdavidxu#include <sys/queue.h> 35201546Sdavidxu#include <sys/mman.h> 36201546Sdavidxu#include <sys/stat.h> 37201546Sdavidxu#include <errno.h> 38201546Sdavidxu#include <machine/atomic.h> 39201546Sdavidxu#include <sys/umtx.h> 40201546Sdavidxu#include <limits.h> 41201546Sdavidxu#include <fcntl.h> 42201546Sdavidxu#include <pthread.h> 43201546Sdavidxu#include <stdarg.h> 44201546Sdavidxu#include <stdlib.h> 45201546Sdavidxu#include <string.h> 46201546Sdavidxu#include <time.h> 47201546Sdavidxu#include <semaphore.h> 48201546Sdavidxu#include <unistd.h> 49201546Sdavidxu#include "un-namespace.h" 50213153Sdavidxu#include "libc_private.h" 51201546Sdavidxu 52201561Sdavidxu__weak_reference(_sem_close, sem_close); 53201561Sdavidxu__weak_reference(_sem_destroy, sem_destroy); 54201561Sdavidxu__weak_reference(_sem_getvalue, sem_getvalue); 55201561Sdavidxu__weak_reference(_sem_init, sem_init); 56201561Sdavidxu__weak_reference(_sem_open, sem_open); 57201561Sdavidxu__weak_reference(_sem_post, sem_post); 58201561Sdavidxu__weak_reference(_sem_timedwait, sem_timedwait); 59201561Sdavidxu__weak_reference(_sem_trywait, sem_trywait); 60201561Sdavidxu__weak_reference(_sem_unlink, sem_unlink); 61201561Sdavidxu__weak_reference(_sem_wait, sem_wait); 62201546Sdavidxu 63201546Sdavidxu#define SEM_PREFIX "/tmp/SEMD" 64233263Sdavidxu#define SEM_MAGIC ((u_int32_t)0x73656d31) 65201546Sdavidxu 66201546Sdavidxustruct sem_nameinfo { 67201546Sdavidxu int open_count; 68201546Sdavidxu char *name; 69201546Sdavidxu sem_t *sem; 70201546Sdavidxu LIST_ENTRY(sem_nameinfo) next; 71201546Sdavidxu}; 72201546Sdavidxu 73201546Sdavidxustatic pthread_once_t once = PTHREAD_ONCE_INIT; 74201546Sdavidxustatic pthread_mutex_t sem_llock; 75201546Sdavidxustatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 76201546Sdavidxu 77201546Sdavidxustatic void 78201546Sdavidxusem_prefork() 79201546Sdavidxu{ 80201546Sdavidxu 81201546Sdavidxu _pthread_mutex_lock(&sem_llock); 82201546Sdavidxu} 83201546Sdavidxu 84201546Sdavidxustatic void 85201546Sdavidxusem_postfork() 86201546Sdavidxu{ 87201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 88201546Sdavidxu} 89201546Sdavidxu 90201546Sdavidxustatic void 91201546Sdavidxusem_child_postfork() 92201546Sdavidxu{ 93201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 94201546Sdavidxu} 95201546Sdavidxu 96201546Sdavidxustatic void 97201546Sdavidxusem_module_init(void) 98201546Sdavidxu{ 99201546Sdavidxu pthread_mutexattr_t ma; 100201546Sdavidxu 101201546Sdavidxu _pthread_mutexattr_init(&ma); 102201546Sdavidxu _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 103201546Sdavidxu _pthread_mutex_init(&sem_llock, &ma); 104201546Sdavidxu _pthread_mutexattr_destroy(&ma); 105201546Sdavidxu _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 106201546Sdavidxu} 107201546Sdavidxu 108201546Sdavidxustatic inline int 109201546Sdavidxusem_check_validity(sem_t *sem) 110201546Sdavidxu{ 111201546Sdavidxu 112233263Sdavidxu if (sem->_magic == SEM_MAGIC) 113201546Sdavidxu return (0); 114201546Sdavidxu else { 115201546Sdavidxu errno = EINVAL; 116201546Sdavidxu return (-1); 117201546Sdavidxu } 118201546Sdavidxu} 119201546Sdavidxu 120201546Sdavidxuint 121201561Sdavidxu_sem_init(sem_t *sem, int pshared, unsigned int value) 122201546Sdavidxu{ 123201546Sdavidxu 124201546Sdavidxu if (value > SEM_VALUE_MAX) { 125201546Sdavidxu errno = EINVAL; 126201546Sdavidxu return (-1); 127201546Sdavidxu } 128201546Sdavidxu 129201546Sdavidxu bzero(sem, sizeof(sem_t)); 130201546Sdavidxu sem->_magic = SEM_MAGIC; 131201546Sdavidxu sem->_kern._count = (u_int32_t)value; 132201546Sdavidxu sem->_kern._has_waiters = 0; 133233263Sdavidxu sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 134201546Sdavidxu return (0); 135201546Sdavidxu} 136201546Sdavidxu 137201546Sdavidxusem_t * 138201561Sdavidxu_sem_open(const char *name, int flags, ...) 139201546Sdavidxu{ 140201546Sdavidxu char path[PATH_MAX]; 141201546Sdavidxu 142201546Sdavidxu struct stat sb; 143201546Sdavidxu va_list ap; 144201546Sdavidxu struct sem_nameinfo *ni = NULL; 145201546Sdavidxu sem_t *sem = NULL; 146202557Sdavidxu int fd = -1, mode, len, errsave; 147201715Sdavidxu int value = 0; 148201546Sdavidxu 149201546Sdavidxu if (name[0] != '/') { 150201546Sdavidxu errno = EINVAL; 151202185Sdavidxu return (SEM_FAILED); 152201546Sdavidxu } 153201546Sdavidxu name++; 154201546Sdavidxu 155201546Sdavidxu if (flags & ~(O_CREAT|O_EXCL)) { 156201546Sdavidxu errno = EINVAL; 157202185Sdavidxu return (SEM_FAILED); 158201546Sdavidxu } 159201546Sdavidxu 160201546Sdavidxu _pthread_once(&once, sem_module_init); 161201546Sdavidxu 162201546Sdavidxu _pthread_mutex_lock(&sem_llock); 163201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 164201546Sdavidxu if (strcmp(name, ni->name) == 0) { 165234057Sjilles if ((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) { 166234057Sjilles _pthread_mutex_unlock(&sem_llock); 167234057Sjilles errno = EEXIST; 168234057Sjilles return (SEM_FAILED); 169234057Sjilles } else { 170234057Sjilles ni->open_count++; 171234057Sjilles sem = ni->sem; 172234057Sjilles _pthread_mutex_unlock(&sem_llock); 173234057Sjilles return (sem); 174234057Sjilles } 175201546Sdavidxu } 176201546Sdavidxu } 177201546Sdavidxu 178201546Sdavidxu if (flags & O_CREAT) { 179201546Sdavidxu va_start(ap, flags); 180201546Sdavidxu mode = va_arg(ap, int); 181201715Sdavidxu value = va_arg(ap, int); 182201546Sdavidxu va_end(ap); 183201546Sdavidxu } 184201546Sdavidxu 185201546Sdavidxu len = sizeof(*ni) + strlen(name) + 1; 186201546Sdavidxu ni = (struct sem_nameinfo *)malloc(len); 187201546Sdavidxu if (ni == NULL) { 188201546Sdavidxu errno = ENOSPC; 189201546Sdavidxu goto error; 190201546Sdavidxu } 191201546Sdavidxu 192201546Sdavidxu ni->name = (char *)(ni+1); 193201546Sdavidxu strcpy(ni->name, name); 194201546Sdavidxu 195201546Sdavidxu strcpy(path, SEM_PREFIX); 196201546Sdavidxu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 197201546Sdavidxu errno = ENAMETOOLONG; 198201546Sdavidxu goto error; 199201546Sdavidxu } 200201546Sdavidxu 201246872Sdavidxu fd = _open(path, flags|O_RDWR|O_CLOEXEC|O_EXLOCK, mode); 202201546Sdavidxu if (fd == -1) 203201546Sdavidxu goto error; 204246872Sdavidxu if (_fstat(fd, &sb)) 205201546Sdavidxu goto error; 206201546Sdavidxu if (sb.st_size < sizeof(sem_t)) { 207201546Sdavidxu sem_t tmp; 208201546Sdavidxu 209201546Sdavidxu tmp._magic = SEM_MAGIC; 210201546Sdavidxu tmp._kern._has_waiters = 0; 211201715Sdavidxu tmp._kern._count = value; 212233263Sdavidxu tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 213246872Sdavidxu if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 214201546Sdavidxu goto error; 215201546Sdavidxu } 216201546Sdavidxu flock(fd, LOCK_UN); 217201546Sdavidxu sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 218201546Sdavidxu MAP_SHARED|MAP_NOSYNC, fd, 0); 219201546Sdavidxu if (sem == MAP_FAILED) { 220201546Sdavidxu sem = NULL; 221201546Sdavidxu if (errno == ENOMEM) 222201546Sdavidxu errno = ENOSPC; 223201546Sdavidxu goto error; 224201546Sdavidxu } 225201546Sdavidxu if (sem->_magic != SEM_MAGIC) { 226201546Sdavidxu errno = EINVAL; 227201546Sdavidxu goto error; 228201546Sdavidxu } 229201546Sdavidxu ni->open_count = 1; 230201546Sdavidxu ni->sem = sem; 231201546Sdavidxu LIST_INSERT_HEAD(&sem_list, ni, next); 232246894Sdavidxu _close(fd); 233201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 234201546Sdavidxu return (sem); 235201546Sdavidxu 236201546Sdavidxuerror: 237202557Sdavidxu errsave = errno; 238201546Sdavidxu if (fd != -1) 239201546Sdavidxu _close(fd); 240201546Sdavidxu if (sem != NULL) 241201546Sdavidxu munmap(sem, sizeof(sem_t)); 242201546Sdavidxu free(ni); 243246894Sdavidxu _pthread_mutex_unlock(&sem_llock); 244202557Sdavidxu errno = errsave; 245201546Sdavidxu return (SEM_FAILED); 246201546Sdavidxu} 247201546Sdavidxu 248201546Sdavidxuint 249201561Sdavidxu_sem_close(sem_t *sem) 250201546Sdavidxu{ 251201546Sdavidxu struct sem_nameinfo *ni; 252201546Sdavidxu 253201546Sdavidxu if (sem_check_validity(sem) != 0) 254201546Sdavidxu return (-1); 255201546Sdavidxu 256201546Sdavidxu if (!(sem->_kern._flags & SEM_NAMED)) { 257201546Sdavidxu errno = EINVAL; 258201546Sdavidxu return (-1); 259201546Sdavidxu } 260201546Sdavidxu 261202326Sdavidxu _pthread_once(&once, sem_module_init); 262202326Sdavidxu 263201546Sdavidxu _pthread_mutex_lock(&sem_llock); 264201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 265201546Sdavidxu if (sem == ni->sem) { 266201546Sdavidxu if (--ni->open_count > 0) { 267201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 268201546Sdavidxu return (0); 269201546Sdavidxu } 270201546Sdavidxu else 271201546Sdavidxu break; 272201546Sdavidxu } 273201546Sdavidxu } 274201546Sdavidxu 275201546Sdavidxu if (ni) { 276201546Sdavidxu LIST_REMOVE(ni, next); 277201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 278201546Sdavidxu munmap(sem, sizeof(*sem)); 279201546Sdavidxu free(ni); 280201546Sdavidxu return (0); 281201546Sdavidxu } 282201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 283202185Sdavidxu errno = EINVAL; 284201546Sdavidxu return (-1); 285201546Sdavidxu} 286201546Sdavidxu 287201546Sdavidxuint 288201561Sdavidxu_sem_unlink(const char *name) 289201546Sdavidxu{ 290201546Sdavidxu char path[PATH_MAX]; 291201546Sdavidxu 292201546Sdavidxu if (name[0] != '/') { 293201546Sdavidxu errno = ENOENT; 294201546Sdavidxu return -1; 295201546Sdavidxu } 296201546Sdavidxu name++; 297201546Sdavidxu 298201546Sdavidxu strcpy(path, SEM_PREFIX); 299201546Sdavidxu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 300201546Sdavidxu errno = ENAMETOOLONG; 301201546Sdavidxu return (-1); 302201546Sdavidxu } 303201546Sdavidxu return unlink(path); 304201546Sdavidxu} 305201546Sdavidxu 306201546Sdavidxuint 307201561Sdavidxu_sem_destroy(sem_t *sem) 308201546Sdavidxu{ 309201546Sdavidxu 310201546Sdavidxu if (sem_check_validity(sem) != 0) 311201546Sdavidxu return (-1); 312201546Sdavidxu 313201546Sdavidxu if (sem->_kern._flags & SEM_NAMED) { 314201546Sdavidxu errno = EINVAL; 315201546Sdavidxu return (-1); 316201546Sdavidxu } 317201546Sdavidxu sem->_magic = 0; 318201546Sdavidxu return (0); 319201546Sdavidxu} 320201546Sdavidxu 321201546Sdavidxuint 322201561Sdavidxu_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 323201546Sdavidxu{ 324201546Sdavidxu 325201546Sdavidxu if (sem_check_validity(sem) != 0) 326201546Sdavidxu return (-1); 327201546Sdavidxu 328233263Sdavidxu *sval = (int)sem->_kern._count; 329201546Sdavidxu return (0); 330201546Sdavidxu} 331201546Sdavidxu 332201547Sdavidxustatic __inline int 333233263Sdavidxuusem_wake(struct _usem *sem) 334233263Sdavidxu{ 335233263Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 336233263Sdavidxu} 337233263Sdavidxu 338233263Sdavidxustatic __inline int 339232144Sdavidxuusem_wait(struct _usem *sem, const struct timespec *abstime) 340201546Sdavidxu{ 341232144Sdavidxu struct _umtx_time *tm_p, timeout; 342232144Sdavidxu size_t tm_size; 343232144Sdavidxu 344232144Sdavidxu if (abstime == NULL) { 345232144Sdavidxu tm_p = NULL; 346232144Sdavidxu tm_size = 0; 347232144Sdavidxu } else { 348232144Sdavidxu timeout._clockid = CLOCK_REALTIME; 349232144Sdavidxu timeout._flags = UMTX_ABSTIME; 350232144Sdavidxu timeout._timeout = *abstime; 351232144Sdavidxu tm_p = &timeout; 352232144Sdavidxu tm_size = sizeof(timeout); 353201546Sdavidxu } 354232144Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, 355232144Sdavidxu (void *)tm_size, __DECONST(void*, tm_p)); 356201546Sdavidxu} 357201546Sdavidxu 358201546Sdavidxuint 359201561Sdavidxu_sem_trywait(sem_t *sem) 360201546Sdavidxu{ 361233263Sdavidxu int val; 362201546Sdavidxu 363201546Sdavidxu if (sem_check_validity(sem) != 0) 364201546Sdavidxu return (-1); 365233263Sdavidxu 366233263Sdavidxu while ((val = sem->_kern._count) > 0) { 367233263Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 368233263Sdavidxu return (0); 369233263Sdavidxu } 370233263Sdavidxu errno = EAGAIN; 371201546Sdavidxu return (-1); 372201546Sdavidxu} 373201546Sdavidxu 374201546Sdavidxuint 375201561Sdavidxu_sem_timedwait(sem_t * __restrict sem, 376201546Sdavidxu const struct timespec * __restrict abstime) 377201546Sdavidxu{ 378233263Sdavidxu int val, retval; 379201546Sdavidxu 380201546Sdavidxu if (sem_check_validity(sem) != 0) 381201546Sdavidxu return (-1); 382201546Sdavidxu 383201546Sdavidxu retval = 0; 384201546Sdavidxu for (;;) { 385233263Sdavidxu while ((val = sem->_kern._count) > 0) { 386233263Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 387233263Sdavidxu return (0); 388233263Sdavidxu } 389201546Sdavidxu 390213153Sdavidxu if (retval) { 391213153Sdavidxu _pthread_testcancel(); 392201546Sdavidxu break; 393213153Sdavidxu } 394201546Sdavidxu 395201546Sdavidxu /* 396201546Sdavidxu * The timeout argument is only supposed to 397201546Sdavidxu * be checked if the thread would have blocked. 398201546Sdavidxu */ 399201546Sdavidxu if (abstime != NULL) { 400201546Sdavidxu if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 401201546Sdavidxu errno = EINVAL; 402201546Sdavidxu return (-1); 403201546Sdavidxu } 404201546Sdavidxu } 405213153Sdavidxu _pthread_cancel_enter(1); 406232144Sdavidxu retval = usem_wait(&sem->_kern, abstime); 407213153Sdavidxu _pthread_cancel_leave(0); 408201546Sdavidxu } 409201546Sdavidxu return (retval); 410201546Sdavidxu} 411201546Sdavidxu 412201546Sdavidxuint 413201561Sdavidxu_sem_wait(sem_t *sem) 414201546Sdavidxu{ 415201561Sdavidxu return _sem_timedwait(sem, NULL); 416201546Sdavidxu} 417201546Sdavidxu 418201546Sdavidxu/* 419201546Sdavidxu * POSIX: 420201546Sdavidxu * The sem_post() interface is reentrant with respect to signals and may be 421201546Sdavidxu * invoked from a signal-catching function. 422201546Sdavidxu * The implementation does not use lock, so it should be safe. 423201546Sdavidxu */ 424201546Sdavidxuint 425201561Sdavidxu_sem_post(sem_t *sem) 426201546Sdavidxu{ 427233913Sdavidxu unsigned int count; 428201546Sdavidxu 429201546Sdavidxu if (sem_check_validity(sem) != 0) 430201546Sdavidxu return (-1); 431201546Sdavidxu 432233913Sdavidxu do { 433233913Sdavidxu count = sem->_kern._count; 434233913Sdavidxu if (count + 1 > SEM_VALUE_MAX) 435233913Sdavidxu return (EOVERFLOW); 436233913Sdavidxu } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1)); 437233913Sdavidxu (void)usem_wake(&sem->_kern); 438233913Sdavidxu return (0); 439201546Sdavidxu} 440