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