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" 64201546Sdavidxu#define SEM_MAGIC ((u_int32_t)0x73656d31) 65201546Sdavidxu 66201546Sdavidxustruct sem_nameinfo { 67201546Sdavidxu int open_count; 68201546Sdavidxu char *name; 69266327Skib dev_t dev; 70266327Skib 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 114201546Sdavidxu 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; 135201546Sdavidxu 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++; 156266327Skib strcpy(path, SEM_PREFIX); 157266327Skib if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 158266327Skib errno = ENAMETOOLONG; 159266327Skib return (SEM_FAILED); 160266327Skib } 161201546Sdavidxu if (flags & ~(O_CREAT|O_EXCL)) { 162201546Sdavidxu errno = EINVAL; 163202185Sdavidxu return (SEM_FAILED); 164201546Sdavidxu } 165266327Skib if ((flags & O_CREAT) != 0) { 166266327Skib va_start(ap, flags); 167266327Skib mode = va_arg(ap, int); 168266327Skib value = va_arg(ap, int); 169266327Skib va_end(ap); 170266327Skib } 171266327Skib fd = -1; 172201546Sdavidxu _pthread_once(&once, sem_module_init); 173201546Sdavidxu 174201546Sdavidxu _pthread_mutex_lock(&sem_llock); 175201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 176266327Skib if (ni->name != NULL && strcmp(name, ni->name) == 0) { 177266327Skib fd = _open(path, flags | O_RDWR | O_CLOEXEC | 178266327Skib O_EXLOCK, mode); 179266327Skib if (fd == -1 || _fstat(fd, &sb) == -1) 180266327Skib goto error; 181266327Skib if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 182266327Skib O_EXCL) || ni->dev != sb.st_dev || 183266327Skib ni->ino != sb.st_ino) { 184266327Skib ni->name = NULL; 185266327Skib ni = NULL; 186266327Skib break; 187235035Sjilles } 188266327Skib ni->open_count++; 189266327Skib sem = ni->sem; 190266327Skib _pthread_mutex_unlock(&sem_llock); 191266327Skib _close(fd); 192266327Skib 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 206266327Skib if (fd == -1) { 207266327Skib fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 208266327Skib if (fd == -1 || _fstat(fd, &sb) == -1) 209266327Skib 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; 217201546Sdavidxu tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 218266325Skib 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; 236266327Skib ni->dev = sb.st_dev; 237266327Skib ni->ino = sb.st_ino; 238201546Sdavidxu LIST_INSERT_HEAD(&sem_list, ni, next); 239266326Skib _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); 250266326Skib _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 } 309266324Skib 310266324Skib 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 335201546Sdavidxu *sval = (int)sem->_kern._count; 336201546Sdavidxu return (0); 337201546Sdavidxu} 338201546Sdavidxu 339201547Sdavidxustatic __inline int 340201546Sdavidxuusem_wake(struct _usem *sem) 341201546Sdavidxu{ 342201546Sdavidxu if (!sem->_has_waiters) 343201546Sdavidxu return (0); 344201546Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 345201546Sdavidxu} 346201546Sdavidxu 347201547Sdavidxustatic __inline int 348201546Sdavidxuusem_wait(struct _usem *sem, const struct timespec *timeout) 349201546Sdavidxu{ 350201546Sdavidxu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 351201546Sdavidxu timeout->tv_nsec <= 0))) { 352201546Sdavidxu errno = ETIMEDOUT; 353201546Sdavidxu return (-1); 354201546Sdavidxu } 355201546Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, 356201546Sdavidxu __DECONST(void*, timeout)); 357201546Sdavidxu} 358201546Sdavidxu 359201546Sdavidxuint 360201561Sdavidxu_sem_trywait(sem_t *sem) 361201546Sdavidxu{ 362201546Sdavidxu int val; 363201546Sdavidxu 364201546Sdavidxu if (sem_check_validity(sem) != 0) 365201546Sdavidxu return (-1); 366201546Sdavidxu 367201546Sdavidxu while ((val = sem->_kern._count) > 0) { 368201546Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 369201546Sdavidxu return (0); 370201546Sdavidxu } 371201546Sdavidxu errno = EAGAIN; 372201546Sdavidxu return (-1); 373201546Sdavidxu} 374201546Sdavidxu 375201546Sdavidxu#define TIMESPEC_SUB(dst, src, val) \ 376201546Sdavidxu do { \ 377201546Sdavidxu (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 378201546Sdavidxu (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 379201546Sdavidxu if ((dst)->tv_nsec < 0) { \ 380201546Sdavidxu (dst)->tv_sec--; \ 381201546Sdavidxu (dst)->tv_nsec += 1000000000; \ 382201546Sdavidxu } \ 383201546Sdavidxu } while (0) 384201546Sdavidxu 385201546Sdavidxu 386201546Sdavidxuint 387201561Sdavidxu_sem_timedwait(sem_t * __restrict sem, 388201546Sdavidxu const struct timespec * __restrict abstime) 389201546Sdavidxu{ 390201546Sdavidxu struct timespec ts, ts2; 391213153Sdavidxu int val, retval; 392201546Sdavidxu 393201546Sdavidxu if (sem_check_validity(sem) != 0) 394201546Sdavidxu return (-1); 395201546Sdavidxu 396201546Sdavidxu retval = 0; 397263255Sdavidxu _pthread_testcancel(); 398201546Sdavidxu for (;;) { 399201546Sdavidxu while ((val = sem->_kern._count) > 0) { 400201546Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 401201546Sdavidxu return (0); 402201546Sdavidxu } 403201546Sdavidxu 404213153Sdavidxu if (retval) { 405213153Sdavidxu _pthread_testcancel(); 406201546Sdavidxu break; 407213153Sdavidxu } 408201546Sdavidxu 409201546Sdavidxu /* 410201546Sdavidxu * The timeout argument is only supposed to 411201546Sdavidxu * be checked if the thread would have blocked. 412201546Sdavidxu */ 413201546Sdavidxu if (abstime != NULL) { 414201546Sdavidxu if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 415201546Sdavidxu errno = EINVAL; 416201546Sdavidxu return (-1); 417201546Sdavidxu } 418201546Sdavidxu clock_gettime(CLOCK_REALTIME, &ts); 419201546Sdavidxu TIMESPEC_SUB(&ts2, abstime, &ts); 420201546Sdavidxu } 421213153Sdavidxu _pthread_cancel_enter(1); 422201546Sdavidxu retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); 423213153Sdavidxu _pthread_cancel_leave(0); 424201546Sdavidxu } 425201546Sdavidxu return (retval); 426201546Sdavidxu} 427201546Sdavidxu 428201546Sdavidxuint 429201561Sdavidxu_sem_wait(sem_t *sem) 430201546Sdavidxu{ 431201561Sdavidxu return _sem_timedwait(sem, NULL); 432201546Sdavidxu} 433201546Sdavidxu 434201546Sdavidxu/* 435201546Sdavidxu * POSIX: 436201546Sdavidxu * The sem_post() interface is reentrant with respect to signals and may be 437201546Sdavidxu * invoked from a signal-catching function. 438201546Sdavidxu * The implementation does not use lock, so it should be safe. 439201546Sdavidxu */ 440201546Sdavidxuint 441201561Sdavidxu_sem_post(sem_t *sem) 442201546Sdavidxu{ 443201546Sdavidxu 444201546Sdavidxu if (sem_check_validity(sem) != 0) 445201546Sdavidxu return (-1); 446201546Sdavidxu 447201546Sdavidxu atomic_add_rel_int(&sem->_kern._count, 1); 448201547Sdavidxu return usem_wake(&sem->_kern); 449201546Sdavidxu} 450