sem_new.c revision 201561
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: head/lib/libc/gen/sem_new.c 201561 2010-01-05 06:40:27Z davidxu $ 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" 50201546Sdavidxu 51201561Sdavidxu__weak_reference(_sem_close, sem_close); 52201561Sdavidxu__weak_reference(_sem_destroy, sem_destroy); 53201561Sdavidxu__weak_reference(_sem_getvalue, sem_getvalue); 54201561Sdavidxu__weak_reference(_sem_init, sem_init); 55201561Sdavidxu__weak_reference(_sem_open, sem_open); 56201561Sdavidxu__weak_reference(_sem_post, sem_post); 57201561Sdavidxu__weak_reference(_sem_timedwait, sem_timedwait); 58201561Sdavidxu__weak_reference(_sem_trywait, sem_trywait); 59201561Sdavidxu__weak_reference(_sem_unlink, sem_unlink); 60201561Sdavidxu__weak_reference(_sem_wait, sem_wait); 61201546Sdavidxu 62201546Sdavidxu#define SEM_PREFIX "/tmp/SEMD" 63201546Sdavidxu#define SEM_MAGIC ((u_int32_t)0x73656d31) 64201546Sdavidxu 65201546Sdavidxustruct sem_nameinfo { 66201546Sdavidxu int open_count; 67201546Sdavidxu char *name; 68201546Sdavidxu sem_t *sem; 69201546Sdavidxu LIST_ENTRY(sem_nameinfo) next; 70201546Sdavidxu}; 71201546Sdavidxu 72201546Sdavidxustatic pthread_once_t once = PTHREAD_ONCE_INIT; 73201546Sdavidxustatic pthread_mutex_t sem_llock; 74201546Sdavidxustatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 75201546Sdavidxu 76201546Sdavidxustatic void 77201546Sdavidxusem_prefork() 78201546Sdavidxu{ 79201546Sdavidxu 80201546Sdavidxu _pthread_mutex_lock(&sem_llock); 81201546Sdavidxu} 82201546Sdavidxu 83201546Sdavidxustatic void 84201546Sdavidxusem_postfork() 85201546Sdavidxu{ 86201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 87201546Sdavidxu} 88201546Sdavidxu 89201546Sdavidxustatic void 90201546Sdavidxusem_child_postfork() 91201546Sdavidxu{ 92201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 93201546Sdavidxu} 94201546Sdavidxu 95201546Sdavidxustatic void 96201546Sdavidxusem_module_init(void) 97201546Sdavidxu{ 98201546Sdavidxu pthread_mutexattr_t ma; 99201546Sdavidxu 100201546Sdavidxu _pthread_mutexattr_init(&ma); 101201546Sdavidxu _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 102201546Sdavidxu _pthread_mutex_init(&sem_llock, &ma); 103201546Sdavidxu _pthread_mutexattr_destroy(&ma); 104201546Sdavidxu _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 105201546Sdavidxu} 106201546Sdavidxu 107201546Sdavidxustatic inline int 108201546Sdavidxusem_check_validity(sem_t *sem) 109201546Sdavidxu{ 110201546Sdavidxu 111201546Sdavidxu if (sem->_magic == SEM_MAGIC) 112201546Sdavidxu return (0); 113201546Sdavidxu else { 114201546Sdavidxu errno = EINVAL; 115201546Sdavidxu return (-1); 116201546Sdavidxu } 117201546Sdavidxu} 118201546Sdavidxu 119201546Sdavidxuint 120201561Sdavidxu_sem_init(sem_t *sem, int pshared, unsigned int value) 121201546Sdavidxu{ 122201546Sdavidxu 123201546Sdavidxu if (value > SEM_VALUE_MAX) { 124201546Sdavidxu errno = EINVAL; 125201546Sdavidxu return (-1); 126201546Sdavidxu } 127201546Sdavidxu 128201546Sdavidxu bzero(sem, sizeof(sem_t)); 129201546Sdavidxu sem->_magic = SEM_MAGIC; 130201546Sdavidxu sem->_kern._count = (u_int32_t)value; 131201546Sdavidxu sem->_kern._has_waiters = 0; 132201546Sdavidxu sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 133201546Sdavidxu return (0); 134201546Sdavidxu} 135201546Sdavidxu 136201546Sdavidxusem_t * 137201561Sdavidxu_sem_open(const char *name, int flags, ...) 138201546Sdavidxu{ 139201546Sdavidxu char path[PATH_MAX]; 140201546Sdavidxu 141201546Sdavidxu struct stat sb; 142201546Sdavidxu va_list ap; 143201546Sdavidxu struct sem_nameinfo *ni = NULL; 144201546Sdavidxu sem_t *sem = NULL; 145201546Sdavidxu int fd = -1, mode, len; 146201546Sdavidxu 147201546Sdavidxu if (name[0] != '/') { 148201546Sdavidxu errno = EINVAL; 149201546Sdavidxu return (NULL); 150201546Sdavidxu } 151201546Sdavidxu name++; 152201546Sdavidxu 153201546Sdavidxu if (flags & ~(O_CREAT|O_EXCL)) { 154201546Sdavidxu errno = EINVAL; 155201546Sdavidxu return (NULL); 156201546Sdavidxu } 157201546Sdavidxu 158201546Sdavidxu _pthread_once(&once, sem_module_init); 159201546Sdavidxu 160201546Sdavidxu _pthread_mutex_lock(&sem_llock); 161201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 162201546Sdavidxu if (strcmp(name, ni->name) == 0) { 163201546Sdavidxu ni->open_count++; 164201546Sdavidxu sem = ni->sem; 165201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 166201546Sdavidxu return (sem); 167201546Sdavidxu } 168201546Sdavidxu } 169201546Sdavidxu 170201546Sdavidxu if (flags & O_CREAT) { 171201546Sdavidxu va_start(ap, flags); 172201546Sdavidxu mode = va_arg(ap, int); 173201546Sdavidxu va_end(ap); 174201546Sdavidxu } 175201546Sdavidxu 176201546Sdavidxu len = sizeof(*ni) + strlen(name) + 1; 177201546Sdavidxu ni = (struct sem_nameinfo *)malloc(len); 178201546Sdavidxu if (ni == NULL) { 179201546Sdavidxu errno = ENOSPC; 180201546Sdavidxu goto error; 181201546Sdavidxu } 182201546Sdavidxu 183201546Sdavidxu ni->name = (char *)(ni+1); 184201546Sdavidxu strcpy(ni->name, name); 185201546Sdavidxu 186201546Sdavidxu strcpy(path, SEM_PREFIX); 187201546Sdavidxu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 188201546Sdavidxu errno = ENAMETOOLONG; 189201546Sdavidxu goto error; 190201546Sdavidxu } 191201546Sdavidxu 192201546Sdavidxu fd = _open(path, flags|O_RDWR, mode); 193201546Sdavidxu if (fd == -1) 194201546Sdavidxu goto error; 195201546Sdavidxu if (flock(fd, LOCK_EX) == -1) 196201546Sdavidxu goto error; 197201546Sdavidxu if (_fstat(fd, &sb)) { 198201546Sdavidxu flock(fd, LOCK_UN); 199201546Sdavidxu goto error; 200201546Sdavidxu } 201201546Sdavidxu if (sb.st_size < sizeof(sem_t)) { 202201546Sdavidxu sem_t tmp; 203201546Sdavidxu 204201546Sdavidxu tmp._magic = SEM_MAGIC; 205201546Sdavidxu tmp._kern._has_waiters = 0; 206201546Sdavidxu tmp._kern._count = 0; 207201546Sdavidxu tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 208201546Sdavidxu if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { 209201546Sdavidxu flock(fd, LOCK_UN); 210201546Sdavidxu goto error; 211201546Sdavidxu } 212201546Sdavidxu } 213201546Sdavidxu flock(fd, LOCK_UN); 214201546Sdavidxu sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 215201546Sdavidxu MAP_SHARED|MAP_NOSYNC, fd, 0); 216201546Sdavidxu if (sem == MAP_FAILED) { 217201546Sdavidxu sem = NULL; 218201546Sdavidxu if (errno == ENOMEM) 219201546Sdavidxu errno = ENOSPC; 220201546Sdavidxu goto error; 221201546Sdavidxu } 222201546Sdavidxu if (sem->_magic != SEM_MAGIC) { 223201546Sdavidxu errno = EINVAL; 224201546Sdavidxu goto error; 225201546Sdavidxu } 226201546Sdavidxu ni->open_count = 1; 227201546Sdavidxu ni->sem = sem; 228201546Sdavidxu LIST_INSERT_HEAD(&sem_list, ni, next); 229201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 230201546Sdavidxu _close(fd); 231201546Sdavidxu return (sem); 232201546Sdavidxu 233201546Sdavidxuerror: 234201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 235201546Sdavidxu if (fd != -1) 236201546Sdavidxu _close(fd); 237201546Sdavidxu if (sem != NULL) 238201546Sdavidxu munmap(sem, sizeof(sem_t)); 239201546Sdavidxu free(ni); 240201546Sdavidxu return (SEM_FAILED); 241201546Sdavidxu} 242201546Sdavidxu 243201546Sdavidxuint 244201561Sdavidxu_sem_close(sem_t *sem) 245201546Sdavidxu{ 246201546Sdavidxu struct sem_nameinfo *ni; 247201546Sdavidxu 248201546Sdavidxu if (sem_check_validity(sem) != 0) 249201546Sdavidxu return (-1); 250201546Sdavidxu 251201546Sdavidxu if (!(sem->_kern._flags & SEM_NAMED)) { 252201546Sdavidxu errno = EINVAL; 253201546Sdavidxu return (-1); 254201546Sdavidxu } 255201546Sdavidxu 256201546Sdavidxu _pthread_mutex_lock(&sem_llock); 257201546Sdavidxu LIST_FOREACH(ni, &sem_list, next) { 258201546Sdavidxu if (sem == ni->sem) { 259201546Sdavidxu if (--ni->open_count > 0) { 260201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 261201546Sdavidxu return (0); 262201546Sdavidxu } 263201546Sdavidxu else 264201546Sdavidxu break; 265201546Sdavidxu } 266201546Sdavidxu } 267201546Sdavidxu 268201546Sdavidxu if (ni) { 269201546Sdavidxu LIST_REMOVE(ni, next); 270201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 271201546Sdavidxu munmap(sem, sizeof(*sem)); 272201546Sdavidxu free(ni); 273201546Sdavidxu return (0); 274201546Sdavidxu } 275201546Sdavidxu _pthread_mutex_unlock(&sem_llock); 276201546Sdavidxu return (-1); 277201546Sdavidxu} 278201546Sdavidxu 279201546Sdavidxuint 280201561Sdavidxu_sem_unlink(const char *name) 281201546Sdavidxu{ 282201546Sdavidxu char path[PATH_MAX]; 283201546Sdavidxu 284201546Sdavidxu if (name[0] != '/') { 285201546Sdavidxu errno = ENOENT; 286201546Sdavidxu return -1; 287201546Sdavidxu } 288201546Sdavidxu name++; 289201546Sdavidxu 290201546Sdavidxu strcpy(path, SEM_PREFIX); 291201546Sdavidxu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 292201546Sdavidxu errno = ENAMETOOLONG; 293201546Sdavidxu return (-1); 294201546Sdavidxu } 295201546Sdavidxu return unlink(path); 296201546Sdavidxu} 297201546Sdavidxu 298201546Sdavidxuint 299201561Sdavidxu_sem_destroy(sem_t *sem) 300201546Sdavidxu{ 301201546Sdavidxu 302201546Sdavidxu if (sem_check_validity(sem) != 0) 303201546Sdavidxu return (-1); 304201546Sdavidxu 305201546Sdavidxu if (sem->_kern._flags & SEM_NAMED) { 306201546Sdavidxu errno = EINVAL; 307201546Sdavidxu return (-1); 308201546Sdavidxu } 309201546Sdavidxu sem->_magic = 0; 310201546Sdavidxu return (0); 311201546Sdavidxu} 312201546Sdavidxu 313201546Sdavidxuint 314201561Sdavidxu_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 315201546Sdavidxu{ 316201546Sdavidxu 317201546Sdavidxu if (sem_check_validity(sem) != 0) 318201546Sdavidxu return (-1); 319201546Sdavidxu 320201546Sdavidxu *sval = (int)sem->_kern._count; 321201546Sdavidxu return (0); 322201546Sdavidxu} 323201546Sdavidxu 324201547Sdavidxustatic __inline int 325201546Sdavidxuusem_wake(struct _usem *sem) 326201546Sdavidxu{ 327201546Sdavidxu if (!sem->_has_waiters) 328201546Sdavidxu return (0); 329201546Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 330201546Sdavidxu} 331201546Sdavidxu 332201547Sdavidxustatic __inline int 333201546Sdavidxuusem_wait(struct _usem *sem, const struct timespec *timeout) 334201546Sdavidxu{ 335201546Sdavidxu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 336201546Sdavidxu timeout->tv_nsec <= 0))) { 337201546Sdavidxu errno = ETIMEDOUT; 338201546Sdavidxu return (-1); 339201546Sdavidxu } 340201546Sdavidxu return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, 341201546Sdavidxu __DECONST(void*, timeout)); 342201546Sdavidxu} 343201546Sdavidxu 344201546Sdavidxuint 345201561Sdavidxu_sem_trywait(sem_t *sem) 346201546Sdavidxu{ 347201546Sdavidxu int val; 348201546Sdavidxu 349201546Sdavidxu if (sem_check_validity(sem) != 0) 350201546Sdavidxu return (-1); 351201546Sdavidxu 352201546Sdavidxu while ((val = sem->_kern._count) > 0) { 353201546Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 354201546Sdavidxu return (0); 355201546Sdavidxu } 356201546Sdavidxu errno = EAGAIN; 357201546Sdavidxu return (-1); 358201546Sdavidxu} 359201546Sdavidxu 360201546Sdavidxustatic void 361201546Sdavidxusem_cancel_handler(void *arg) 362201546Sdavidxu{ 363201546Sdavidxu sem_t *sem = arg; 364201546Sdavidxu 365201546Sdavidxu if (sem->_kern._has_waiters && sem->_kern._count) 366201546Sdavidxu usem_wake(&sem->_kern); 367201546Sdavidxu} 368201546Sdavidxu 369201546Sdavidxu#define TIMESPEC_SUB(dst, src, val) \ 370201546Sdavidxu do { \ 371201546Sdavidxu (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 372201546Sdavidxu (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 373201546Sdavidxu if ((dst)->tv_nsec < 0) { \ 374201546Sdavidxu (dst)->tv_sec--; \ 375201546Sdavidxu (dst)->tv_nsec += 1000000000; \ 376201546Sdavidxu } \ 377201546Sdavidxu } while (0) 378201546Sdavidxu 379201546Sdavidxu 380201547Sdavidxustatic __inline int 381201546Sdavidxuenable_async_cancel(void) 382201546Sdavidxu{ 383201546Sdavidxu int old; 384201546Sdavidxu 385201546Sdavidxu _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); 386201546Sdavidxu return (old); 387201546Sdavidxu} 388201546Sdavidxu 389201547Sdavidxustatic __inline void 390201546Sdavidxurestore_async_cancel(int val) 391201546Sdavidxu{ 392201546Sdavidxu _pthread_setcanceltype(val, NULL); 393201546Sdavidxu} 394201546Sdavidxu 395201546Sdavidxuint 396201561Sdavidxu_sem_timedwait(sem_t * __restrict sem, 397201546Sdavidxu const struct timespec * __restrict abstime) 398201546Sdavidxu{ 399201546Sdavidxu struct timespec ts, ts2; 400201546Sdavidxu int val, retval, saved_cancel; 401201546Sdavidxu 402201546Sdavidxu if (sem_check_validity(sem) != 0) 403201546Sdavidxu return (-1); 404201546Sdavidxu 405201546Sdavidxu retval = 0; 406201546Sdavidxu for (;;) { 407201546Sdavidxu while ((val = sem->_kern._count) > 0) { 408201546Sdavidxu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 409201546Sdavidxu return (0); 410201546Sdavidxu } 411201546Sdavidxu 412201546Sdavidxu if (retval) 413201546Sdavidxu break; 414201546Sdavidxu 415201546Sdavidxu /* 416201546Sdavidxu * The timeout argument is only supposed to 417201546Sdavidxu * be checked if the thread would have blocked. 418201546Sdavidxu */ 419201546Sdavidxu if (abstime != NULL) { 420201546Sdavidxu if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 421201546Sdavidxu errno = EINVAL; 422201546Sdavidxu return (-1); 423201546Sdavidxu } 424201546Sdavidxu clock_gettime(CLOCK_REALTIME, &ts); 425201546Sdavidxu TIMESPEC_SUB(&ts2, abstime, &ts); 426201546Sdavidxu } 427201546Sdavidxu pthread_cleanup_push(sem_cancel_handler, sem); 428201546Sdavidxu saved_cancel = enable_async_cancel(); 429201546Sdavidxu retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); 430201546Sdavidxu restore_async_cancel(saved_cancel); 431201546Sdavidxu pthread_cleanup_pop(0); 432201546Sdavidxu } 433201546Sdavidxu return (retval); 434201546Sdavidxu} 435201546Sdavidxu 436201546Sdavidxuint 437201561Sdavidxu_sem_wait(sem_t *sem) 438201546Sdavidxu{ 439201561Sdavidxu return _sem_timedwait(sem, NULL); 440201546Sdavidxu} 441201546Sdavidxu 442201546Sdavidxu/* 443201546Sdavidxu * POSIX: 444201546Sdavidxu * The sem_post() interface is reentrant with respect to signals and may be 445201546Sdavidxu * invoked from a signal-catching function. 446201546Sdavidxu * The implementation does not use lock, so it should be safe. 447201546Sdavidxu */ 448201546Sdavidxuint 449201561Sdavidxu_sem_post(sem_t *sem) 450201546Sdavidxu{ 451201546Sdavidxu 452201546Sdavidxu if (sem_check_validity(sem) != 0) 453201546Sdavidxu return (-1); 454201546Sdavidxu 455201546Sdavidxu atomic_add_rel_int(&sem->_kern._count, 1); 456201547Sdavidxu return usem_wake(&sem->_kern); 457201546Sdavidxu} 458