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; 69266306Skib dev_t dev; 70266306Skib ino_t ino; 71201546Sdavidxu sem_t *sem; 72201546Sdavidxu LIST_ENTRY(sem_nameinfo) next; 73201546Sdavidxu}; 74201546Sdavidxu 75201546Sdavidxustatic pthread_once_t once = PTHREAD_ONCE_INIT; 76201546Sdavidxustatic pthread_mutex_t sem_llock; 77201546Sdavidxustatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 78201546Sdavidxu 79201546Sdavidxustatic void 80201546Sdavidxusem_prefork() 81201546Sdavidxu{ 82201546Sdavidxu 83201546Sdavidxu _pthread_mutex_lock(&sem_llock); 84201546Sdavidxu} 85201546Sdavidxu 86201546Sdavidxustatic void 87201546Sdavidxusem_postfork() 88201546Sdavidxu{ 89201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 90201546Sdavidxu} 91201546Sdavidxu 92201546Sdavidxustatic void 93201546Sdavidxusem_child_postfork() 94201546Sdavidxu{ 95201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 96201546Sdavidxu} 97201546Sdavidxu 98201546Sdavidxustatic void 99201546Sdavidxusem_module_init(void) 100201546Sdavidxu{ 101201546Sdavidxu pthread_mutexattr_t ma; 102201546Sdavidxu 103201546Sdavidxu _pthread_mutexattr_init(&ma); 104201546Sdavidxu _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 105201546Sdavidxu _pthread_mutex_init(&sem_llock, &ma); 106201546Sdavidxu _pthread_mutexattr_destroy(&ma); 107201546Sdavidxu _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 108201546Sdavidxu} 109201546Sdavidxu 110201546Sdavidxustatic inline int 111201546Sdavidxusem_check_validity(sem_t *sem) 112201546Sdavidxu{ 113201546Sdavidxu 114233263Sdavidxu if (sem->_magic == SEM_MAGIC) 115201546Sdavidxu return (0); 116201546Sdavidxu else { 117201546Sdavidxu errno = EINVAL; 118201546Sdavidxu return (-1); 119201546Sdavidxu } 120201546Sdavidxu} 121201546Sdavidxu 122201546Sdavidxuint 123201561Sdavidxu_sem_init(sem_t *sem, int pshared, unsigned int value) 124201546Sdavidxu{ 125201546Sdavidxu 126201546Sdavidxu if (value > SEM_VALUE_MAX) { 127201546Sdavidxu errno = EINVAL; 128201546Sdavidxu return (-1); 129201546Sdavidxu } 130201546Sdavidxu 131201546Sdavidxu bzero(sem, sizeof(sem_t)); 132201546Sdavidxu sem->_magic = SEM_MAGIC; 133201546Sdavidxu sem->_kern._count = (u_int32_t)value; 134201546Sdavidxu sem->_kern._has_waiters = 0; 135233263Sdavidxu sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 136201546Sdavidxu return (0); 137201546Sdavidxu} 138201546Sdavidxu 139201546Sdavidxusem_t * 140201561Sdavidxu_sem_open(const char *name, int flags, ...) 141201546Sdavidxu{ 142201546Sdavidxu char path[PATH_MAX]; 143201546Sdavidxu 144201546Sdavidxu struct stat sb; 145201546Sdavidxu va_list ap; 146201546Sdavidxu struct sem_nameinfo *ni = NULL; 147201546Sdavidxu sem_t *sem = NULL; 148202557Sdavidxu int fd = -1, mode, len, errsave; 149201715Sdavidxu int value = 0; 150201546Sdavidxu 151201546Sdavidxu if (name[0] != '/') { 152201546Sdavidxu errno = EINVAL; 153202185Sdavidxu return (SEM_FAILED); 154201546Sdavidxu } 155201546Sdavidxu name++; 156266306Skib strcpy(path, SEM_PREFIX); 157266306Skib if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 158266306Skib errno = ENAMETOOLONG; 159266306Skib return (SEM_FAILED); 160266306Skib } 161201546Sdavidxu if (flags & ~(O_CREAT|O_EXCL)) { 162201546Sdavidxu errno = EINVAL; 163202185Sdavidxu return (SEM_FAILED); 164201546Sdavidxu } 165266306Skib if ((flags & O_CREAT) != 0) { 166266306Skib va_start(ap, flags); 167266306Skib mode = va_arg(ap, int); 168266306Skib value = va_arg(ap, int); 169266306Skib va_end(ap); 170266306Skib } 171266306Skib fd = -1; 172201546Sdavidxu _pthread_once(&once, sem_module_init); 173201546Sdavidxu 174201546Sdavidxu _pthread_mutex_lock(&sem_llock); 175201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 176266306Skib if (ni->name != NULL && strcmp(name, ni->name) == 0) { 177266306Skib fd = _open(path, flags | O_RDWR | O_CLOEXEC | 178266306Skib O_EXLOCK, mode); 179266306Skib if (fd == -1 || _fstat(fd, &sb) == -1) 180266306Skib goto error; 181266306Skib if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 182266306Skib O_EXCL) || ni->dev != sb.st_dev || 183266306Skib ni->ino != sb.st_ino) { 184266306Skib ni->name = NULL; 185266306Skib ni = NULL; 186266306Skib break; 187234057Sjilles } 188266306Skib ni->open_count++; 189266306Skib sem = ni->sem; 190266306Skib _pthread_mutex_unlock(&sem_llock); 191266306Skib _close(fd); 192266306Skib return (sem); 193201546Sdavidxu } 194201546Sdavidxu } 195201546Sdavidxu 196201546Sdavidxu len = sizeof(*ni) + strlen(name) + 1; 197201546Sdavidxu ni = (struct sem_nameinfo *)malloc(len); 198201546Sdavidxu if (ni == NULL) { 199201546Sdavidxu errno = ENOSPC; 200201546Sdavidxu goto error; 201201546Sdavidxu } 202201546Sdavidxu 203201546Sdavidxu ni->name = (char *)(ni+1); 204201546Sdavidxu strcpy(ni->name, name); 205201546Sdavidxu 206266306Skib if (fd == -1) { 207266306Skib fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 208266306Skib if (fd == -1 || _fstat(fd, &sb) == -1) 209266306Skib goto error; 210201546Sdavidxu } 211201546Sdavidxu if (sb.st_size < sizeof(sem_t)) { 212201546Sdavidxu sem_t tmp; 213201546Sdavidxu 214201546Sdavidxu tmp._magic = SEM_MAGIC; 215201546Sdavidxu tmp._kern._has_waiters = 0; 216201715Sdavidxu tmp._kern._count = value; 217233263Sdavidxu tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 218246872Sdavidxu if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 219201546Sdavidxu goto error; 220201546Sdavidxu } 221201546Sdavidxu flock(fd, LOCK_UN); 222201546Sdavidxu sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 223201546Sdavidxu MAP_SHARED|MAP_NOSYNC, fd, 0); 224201546Sdavidxu if (sem == MAP_FAILED) { 225201546Sdavidxu sem = NULL; 226201546Sdavidxu if (errno == ENOMEM) 227201546Sdavidxu errno = ENOSPC; 228201546Sdavidxu goto error; 229201546Sdavidxu } 230201546Sdavidxu if (sem->_magic != SEM_MAGIC) { 231201546Sdavidxu errno = EINVAL; 232201546Sdavidxu goto error; 233201546Sdavidxu } 234201546Sdavidxu ni->open_count = 1; 235201546Sdavidxu ni->sem = sem; 236266306Skib ni->dev = sb.st_dev; 237266306Skib ni->ino = sb.st_ino; 238201546Sdavidxu LIST_INSERT_HEAD(&sem_list, ni, next); 239246894Sdavidxu _close(fd); 240201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 241201546Sdavidxu return (sem); 242201546Sdavidxu 243201546Sdavidxuerror: 244202557Sdavidxu errsave = errno; 245201546Sdavidxu if (fd != -1) 246201546Sdavidxu _close(fd); 247201546Sdavidxu if (sem != NULL) 248201546Sdavidxu munmap(sem, sizeof(sem_t)); 249201546Sdavidxu free(ni); 250246894Sdavidxu _pthread_mutex_unlock(&sem_llock); 251202557Sdavidxu errno = errsave; 252201546Sdavidxu return (SEM_FAILED); 253201546Sdavidxu} 254201546Sdavidxu 255201546Sdavidxuint 256201561Sdavidxu_sem_close(sem_t *sem) 257201546Sdavidxu{ 258201546Sdavidxu struct sem_nameinfo *ni; 259201546Sdavidxu 260201546Sdavidxu if (sem_check_validity(sem) != 0) 261201546Sdavidxu return (-1); 262201546Sdavidxu 263201546Sdavidxu if (!(sem->_kern._flags & SEM_NAMED)) { 264201546Sdavidxu errno = EINVAL; 265201546Sdavidxu return (-1); 266201546Sdavidxu } 267201546Sdavidxu 268202326Sdavidxu _pthread_once(&once, sem_module_init); 269202326Sdavidxu 270201546Sdavidxu _pthread_mutex_lock(&sem_llock); 271201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 272201546Sdavidxu if (sem == ni->sem) { 273201546Sdavidxu if (--ni->open_count > 0) { 274201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 275201546Sdavidxu return (0); 276201546Sdavidxu } 277201546Sdavidxu else 278201546Sdavidxu break; 279201546Sdavidxu } 280201546Sdavidxu } 281201546Sdavidxu 282201546Sdavidxu if (ni) { 283201546Sdavidxu LIST_REMOVE(ni, next); 284201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 285201546Sdavidxu munmap(sem, sizeof(*sem)); 286201546Sdavidxu free(ni); 287201546Sdavidxu return (0); 288201546Sdavidxu } 289201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 290202185Sdavidxu errno = EINVAL; 291201546Sdavidxu return (-1); 292201546Sdavidxu} 293201546Sdavidxu 294201546Sdavidxuint 295201561Sdavidxu_sem_unlink(const char *name) 296201546Sdavidxu{ 297201546Sdavidxu char path[PATH_MAX]; 298201546Sdavidxu 299201546Sdavidxu if (name[0] != '/') { 300201546Sdavidxu errno = ENOENT; 301201546Sdavidxu return -1; 302201546Sdavidxu } 303201546Sdavidxu name++; 304201546Sdavidxu strcpy(path, SEM_PREFIX); 305201546Sdavidxu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 306201546Sdavidxu errno = ENAMETOOLONG; 307201546Sdavidxu return (-1); 308201546Sdavidxu } 309266305Skib 310266305Skib return (unlink(path)); 311201546Sdavidxu} 312201546Sdavidxu 313201546Sdavidxuint 314201561Sdavidxu_sem_destroy(sem_t *sem) 315201546Sdavidxu{ 316201546Sdavidxu 317201546Sdavidxu if (sem_check_validity(sem) != 0) 318201546Sdavidxu return (-1); 319201546Sdavidxu 320201546Sdavidxu if (sem->_kern._flags & SEM_NAMED) { 321201546Sdavidxu errno = EINVAL; 322201546Sdavidxu return (-1); 323201546Sdavidxu } 324201546Sdavidxu sem->_magic = 0; 325201546Sdavidxu return (0); 326201546Sdavidxu} 327201546Sdavidxu 328201546Sdavidxuint 329201561Sdavidxu_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 330201546Sdavidxu{ 331201546Sdavidxu 332201546Sdavidxu if (sem_check_validity(sem) != 0) 333201546Sdavidxu return (-1); 334201546Sdavidxu 335233263Sdavidxu *sval = (int)sem->_kern._count; 336201546Sdavidxu return (0); 337201546Sdavidxu} 338201546Sdavidxu 339201547Sdavidxustatic __inline int 340233263Sdavidxuusem_wake(struct _usem *sem) 341233263Sdavidxu{ 342233263Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 343233263Sdavidxu} 344233263Sdavidxu 345233263Sdavidxustatic __inline int 346232144Sdavidxuusem_wait(struct _usem *sem, const struct timespec *abstime) 347201546Sdavidxu{ 348232144Sdavidxu struct _umtx_time *tm_p, timeout; 349232144Sdavidxu size_t tm_size; 350232144Sdavidxu 351232144Sdavidxu if (abstime == NULL) { 352232144Sdavidxu tm_p = NULL; 353232144Sdavidxu tm_size = 0; 354232144Sdavidxu } else { 355232144Sdavidxu timeout._clockid = CLOCK_REALTIME; 356232144Sdavidxu timeout._flags = UMTX_ABSTIME; 357232144Sdavidxu timeout._timeout = *abstime; 358232144Sdavidxu tm_p = &timeout; 359232144Sdavidxu tm_size = sizeof(timeout); 360201546Sdavidxu } 361232144Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, 362232144Sdavidxu (void *)tm_size, __DECONST(void*, tm_p)); 363201546Sdavidxu} 364201546Sdavidxu 365201546Sdavidxuint 366201561Sdavidxu_sem_trywait(sem_t *sem) 367201546Sdavidxu{ 368233263Sdavidxu int val; 369201546Sdavidxu 370201546Sdavidxu if (sem_check_validity(sem) != 0) 371201546Sdavidxu return (-1); 372233263Sdavidxu 373233263Sdavidxu while ((val = sem->_kern._count) > 0) { 374233263Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 375233263Sdavidxu return (0); 376233263Sdavidxu } 377233263Sdavidxu errno = EAGAIN; 378201546Sdavidxu return (-1); 379201546Sdavidxu} 380201546Sdavidxu 381201546Sdavidxuint 382201561Sdavidxu_sem_timedwait(sem_t * __restrict sem, 383201546Sdavidxu const struct timespec * __restrict abstime) 384201546Sdavidxu{ 385233263Sdavidxu int val, retval; 386201546Sdavidxu 387201546Sdavidxu if (sem_check_validity(sem) != 0) 388201546Sdavidxu return (-1); 389201546Sdavidxu 390201546Sdavidxu retval = 0; 391263258Sdavidxu _pthread_testcancel(); 392201546Sdavidxu for (;;) { 393233263Sdavidxu while ((val = sem->_kern._count) > 0) { 394233263Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 395233263Sdavidxu return (0); 396233263Sdavidxu } 397201546Sdavidxu 398213153Sdavidxu if (retval) { 399213153Sdavidxu _pthread_testcancel(); 400201546Sdavidxu break; 401213153Sdavidxu } 402201546Sdavidxu 403201546Sdavidxu /* 404201546Sdavidxu * The timeout argument is only supposed to 405201546Sdavidxu * be checked if the thread would have blocked. 406201546Sdavidxu */ 407201546Sdavidxu if (abstime != NULL) { 408201546Sdavidxu if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 409201546Sdavidxu errno = EINVAL; 410201546Sdavidxu return (-1); 411201546Sdavidxu } 412201546Sdavidxu } 413213153Sdavidxu _pthread_cancel_enter(1); 414232144Sdavidxu retval = usem_wait(&sem->_kern, abstime); 415213153Sdavidxu _pthread_cancel_leave(0); 416201546Sdavidxu } 417201546Sdavidxu return (retval); 418201546Sdavidxu} 419201546Sdavidxu 420201546Sdavidxuint 421201561Sdavidxu_sem_wait(sem_t *sem) 422201546Sdavidxu{ 423201561Sdavidxu return _sem_timedwait(sem, NULL); 424201546Sdavidxu} 425201546Sdavidxu 426201546Sdavidxu/* 427201546Sdavidxu * POSIX: 428201546Sdavidxu * The sem_post() interface is reentrant with respect to signals and may be 429201546Sdavidxu * invoked from a signal-catching function. 430201546Sdavidxu * The implementation does not use lock, so it should be safe. 431201546Sdavidxu */ 432201546Sdavidxuint 433201561Sdavidxu_sem_post(sem_t *sem) 434201546Sdavidxu{ 435233913Sdavidxu unsigned int count; 436201546Sdavidxu 437201546Sdavidxu if (sem_check_validity(sem) != 0) 438201546Sdavidxu return (-1); 439201546Sdavidxu 440233913Sdavidxu do { 441233913Sdavidxu count = sem->_kern._count; 442233913Sdavidxu if (count + 1 > SEM_VALUE_MAX) 443233913Sdavidxu return (EOVERFLOW); 444233913Sdavidxu } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1)); 445233913Sdavidxu (void)usem_wake(&sem->_kern); 446233913Sdavidxu return (0); 447201546Sdavidxu} 448