sem_new.c revision 265847
150477Speter/* 2139749Simp * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3196008Smjacob * All rights reserved. 4167403Smjacob * 5167403Smjacob * Redistribution and use in source and binary forms, with or without 6167403Smjacob * modification, are permitted provided that the following conditions 7167403Smjacob * are met: 8167403Smjacob * 1. Redistributions of source code must retain the above copyright 9167403Smjacob * notice(s), this list of conditions and the following disclaimer as 10167403Smjacob * the first lines of this file unmodified other than the possible 11167403Smjacob * addition of one or more copyright notices. 12167403Smjacob * 2. Redistributions in binary form must reproduce the above copyright 13167403Smjacob * notice(s), this list of conditions and the following disclaimer in 14167403Smjacob * the documentation and/or other materials provided with the 15167403Smjacob * distribution. 16167403Smjacob * 17167403Smjacob * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18167403Smjacob * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19167403Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20167403Smjacob * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21167403Smjacob * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22167403Smjacob * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23167403Smjacob * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24167403Smjacob * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25167403Smjacob * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26167403Smjacob * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27196008Smjacob * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28167403Smjacob * 29197373Smjacob * $FreeBSD: head/lib/libc/gen/sem_new.c 265847 2014-05-10 19:08:07Z kib $ 30167403Smjacob */ 3139235Sgibbs 3235388Smjacob#include "namespace.h" 3335388Smjacob#include <sys/types.h> 3435388Smjacob#include <sys/queue.h> 3535388Smjacob#include <sys/mman.h> 3635388Smjacob#include <sys/stat.h> 3735388Smjacob#include <errno.h> 3835388Smjacob#include <machine/atomic.h> 3935388Smjacob#include <sys/umtx.h> 4035388Smjacob#include <limits.h> 4135388Smjacob#include <fcntl.h> 4235388Smjacob#include <pthread.h> 4335388Smjacob#include <stdarg.h> 4435388Smjacob#include <stdlib.h> 4535388Smjacob#include <string.h> 4635388Smjacob#include <time.h> 4735388Smjacob#include <semaphore.h> 48165308Smjacob#include <unistd.h> 4935388Smjacob#include "un-namespace.h" 50163899Smjacob#include "libc_private.h" 5135388Smjacob 52160080Smjacob__weak_reference(_sem_close, sem_close); 5335388Smjacob__weak_reference(_sem_destroy, sem_destroy); 5490754Smjacob__weak_reference(_sem_getvalue, sem_getvalue); 5535388Smjacob__weak_reference(_sem_init, sem_init); 5635388Smjacob__weak_reference(_sem_open, sem_open); 5735388Smjacob__weak_reference(_sem_post, sem_post); 5835388Smjacob__weak_reference(_sem_timedwait, sem_timedwait); 5935388Smjacob__weak_reference(_sem_trywait, sem_trywait); 6035388Smjacob__weak_reference(_sem_unlink, sem_unlink); 6135388Smjacob__weak_reference(_sem_wait, sem_wait); 6235388Smjacob 6335388Smjacob#define SEM_PREFIX "/tmp/SEMD" 6435388Smjacob#define SEM_MAGIC ((u_int32_t)0x73656d31) 6535388Smjacob 6635388Smjacobstruct sem_nameinfo { 6735388Smjacob int open_count; 6835388Smjacob char *name; 6935388Smjacob dev_t dev; 7035388Smjacob ino_t ino; 7135388Smjacob sem_t *sem; 7235388Smjacob LIST_ENTRY(sem_nameinfo) next; 7335388Smjacob}; 7435388Smjacob 7535388Smjacobstatic pthread_once_t once = PTHREAD_ONCE_INIT; 7635388Smjacobstatic pthread_mutex_t sem_llock; 7735388Smjacobstatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 7835388Smjacob 7981987Smjacobstatic void 8035388Smjacobsem_prefork() 8135388Smjacob{ 8246966Smjacob 8335388Smjacob _pthread_mutex_lock(&sem_llock); 8435388Smjacob} 8535388Smjacob 8635388Smjacobstatic void 8735388Smjacobsem_postfork() 8835388Smjacob{ 8935388Smjacob _pthread_mutex_unlock(&sem_llock); 9035388Smjacob} 9135388Smjacob 9235388Smjacobstatic void 9346966Smjacobsem_child_postfork() 9435388Smjacob{ 9535388Smjacob _pthread_mutex_unlock(&sem_llock); 9635388Smjacob} 9735388Smjacob 9835388Smjacobstatic void 9946966Smjacobsem_module_init(void) 10035388Smjacob{ 10135388Smjacob pthread_mutexattr_t ma; 10235388Smjacob 10335388Smjacob _pthread_mutexattr_init(&ma); 10435388Smjacob _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 10535388Smjacob _pthread_mutex_init(&sem_llock, &ma); 10635388Smjacob _pthread_mutexattr_destroy(&ma); 10735388Smjacob _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 10843420Smjacob} 10943420Smjacob 11090224Smjacobstatic inline int 11143420Smjacobsem_check_validity(sem_t *sem) 11290224Smjacob{ 11390224Smjacob 11435388Smjacob if (sem->_magic == SEM_MAGIC) 115103825Smjacob return (0); 116103825Smjacob else { 117103825Smjacob errno = EINVAL; 11884241Smjacob return (-1); 11955363Smjacob } 12075194Smjacob} 12175194Smjacob 12284241Smjacobint 12355138Smjacob_sem_init(sem_t *sem, int pshared, unsigned int value) 12484241Smjacob{ 12584241Smjacob 126196008Smjacob if (value > SEM_VALUE_MAX) { 127196008Smjacob errno = EINVAL; 128196008Smjacob return (-1); 129196008Smjacob } 130196008Smjacob 131196008Smjacob bzero(sem, sizeof(sem_t)); 13284241Smjacob sem->_magic = SEM_MAGIC; 13384241Smjacob sem->_kern._count = (u_int32_t)value; 13484241Smjacob sem->_kern._has_waiters = 0; 135163899Smjacob sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 13684241Smjacob return (0); 137196008Smjacob} 138196008Smjacob 139196008Smjacobsem_t * 14084241Smjacob_sem_open(const char *name, int flags, ...) 14184241Smjacob{ 14284241Smjacob char path[PATH_MAX]; 14384241Smjacob 14484241Smjacob struct stat sb; 14584241Smjacob va_list ap; 14684241Smjacob struct sem_nameinfo *ni = NULL; 14784241Smjacob sem_t *sem = NULL; 14884241Smjacob int fd = -1, mode, len, errsave; 14984241Smjacob int value = 0; 15084241Smjacob 15184241Smjacob if (name[0] != '/') { 15284241Smjacob errno = EINVAL; 15384241Smjacob return (SEM_FAILED); 15484241Smjacob } 15584241Smjacob name++; 15684241Smjacob strcpy(path, SEM_PREFIX); 15784241Smjacob if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 15884241Smjacob errno = ENAMETOOLONG; 159290104Smav return (SEM_FAILED); 160290104Smav } 161151834Smjacob if (flags & ~(O_CREAT|O_EXCL)) { 16235388Smjacob errno = EINVAL; 16398282Smjacob return (SEM_FAILED); 16498282Smjacob } 16598282Smjacob if ((flags & O_CREAT) != 0) { 166163899Smjacob va_start(ap, flags); 167163899Smjacob mode = va_arg(ap, int); 168163899Smjacob value = va_arg(ap, int); 169163899Smjacob va_end(ap); 17084241Smjacob } 17184241Smjacob fd = -1; 17284241Smjacob _pthread_once(&once, sem_module_init); 173163899Smjacob 174196008Smjacob _pthread_mutex_lock(&sem_llock); 17584241Smjacob LIST_FOREACH(ni, &sem_list, next) { 17684241Smjacob if (ni->name != NULL && strcmp(name, ni->name) == 0) { 17735388Smjacob fd = _open(path, flags | O_RDWR | O_CLOEXEC | 17835388Smjacob O_EXLOCK, mode); 17935388Smjacob if (fd == -1 || _fstat(fd, &sb) == -1) 18035388Smjacob goto error; 18135388Smjacob if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 18239235Sgibbs O_EXCL) || ni->dev != sb.st_dev || 18339235Sgibbs ni->ino != sb.st_ino) { 18439235Sgibbs ni->name = NULL; 18539235Sgibbs ni = NULL; 18639235Sgibbs break; 18739235Sgibbs } 18839235Sgibbs ni->open_count++; 18939235Sgibbs sem = ni->sem; 19057587Smjacob _pthread_mutex_unlock(&sem_llock); 19157587Smjacob _close(fd); 19257587Smjacob return (sem); 19357587Smjacob } 194290147Smav } 195290147Smav 196290147Smav len = sizeof(*ni) + strlen(name) + 1; 197290147Smav ni = (struct sem_nameinfo *)malloc(len); 198163899Smjacob if (ni == NULL) { 199163899Smjacob errno = ENOSPC; 200163899Smjacob goto error; 201163899Smjacob } 202290147Smav 203290147Smav ni->name = (char *)(ni+1); 204290147Smav strcpy(ni->name, name); 20539235Sgibbs 20639235Sgibbs if (fd == -1) { 20739235Sgibbs fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 20839235Sgibbs if (fd == -1 || _fstat(fd, &sb) == -1) 20939235Sgibbs goto error; 21039235Sgibbs } 21139235Sgibbs if (sb.st_size < sizeof(sem_t)) { 21239235Sgibbs sem_t tmp; 21339235Sgibbs 21439235Sgibbs tmp._magic = SEM_MAGIC; 21541520Smjacob tmp._kern._has_waiters = 0; 21639235Sgibbs tmp._kern._count = value; 21739235Sgibbs tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 21839235Sgibbs if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 21939235Sgibbs goto error; 22039235Sgibbs } 22139235Sgibbs flock(fd, LOCK_UN); 22239235Sgibbs sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 22339235Sgibbs MAP_SHARED|MAP_NOSYNC, fd, 0); 22439235Sgibbs if (sem == MAP_FAILED) { 22541520Smjacob sem = NULL; 22639235Sgibbs if (errno == ENOMEM) 22781792Smjacob errno = ENOSPC; 228163899Smjacob goto error; 229163899Smjacob } 23079237Smjacob if (sem->_magic != SEM_MAGIC) { 23179237Smjacob errno = EINVAL; 232204397Smjacob goto error; 233204397Smjacob } 23479237Smjacob ni->open_count = 1; 23579237Smjacob ni->sem = sem; 23679237Smjacob ni->dev = sb.st_dev; 23779237Smjacob ni->ino = sb.st_ino; 23879237Smjacob LIST_INSERT_HEAD(&sem_list, ni, next); 23979237Smjacob _close(fd); 24057149Smjacob _pthread_mutex_unlock(&sem_llock); 241204397Smjacob return (sem); 242204397Smjacob 243204397Smjacoberror: 244204397Smjacob errsave = errno; 245204397Smjacob if (fd != -1) 24657149Smjacob _close(fd); 24757149Smjacob if (sem != NULL) 24857149Smjacob munmap(sem, sizeof(sem_t)); 24957149Smjacob free(ni); 25057149Smjacob _pthread_mutex_unlock(&sem_llock); 25157149Smjacob errno = errsave; 252204397Smjacob return (SEM_FAILED); 253204397Smjacob} 254163899Smjacob 255163899Smjacobint 25679237Smjacob_sem_close(sem_t *sem) 257197372Smjacob{ 258197372Smjacob struct sem_nameinfo *ni; 259197372Smjacob 260197372Smjacob if (sem_check_validity(sem) != 0) 261197372Smjacob return (-1); 262197372Smjacob 263197372Smjacob if (!(sem->_kern._flags & SEM_NAMED)) { 264197372Smjacob errno = EINVAL; 265197372Smjacob return (-1); 266197372Smjacob } 267197372Smjacob 268197372Smjacob _pthread_once(&once, sem_module_init); 269197372Smjacob 270197372Smjacob _pthread_mutex_lock(&sem_llock); 271197372Smjacob LIST_FOREACH(ni, &sem_list, next) { 272197372Smjacob if (sem == ni->sem) { 273197372Smjacob if (--ni->open_count > 0) { 274197372Smjacob _pthread_mutex_unlock(&sem_llock); 275197372Smjacob return (0); 276197372Smjacob } 277197372Smjacob else 278197372Smjacob break; 27979237Smjacob } 28079237Smjacob } 28179237Smjacob 28279237Smjacob if (ni) { 28357149Smjacob LIST_REMOVE(ni, next); 28439235Sgibbs _pthread_mutex_unlock(&sem_llock); 285197372Smjacob munmap(sem, sizeof(*sem)); 286197372Smjacob free(ni); 287197372Smjacob return (0); 288197372Smjacob } 289163899Smjacob _pthread_mutex_unlock(&sem_llock); 29079237Smjacob errno = EINVAL; 291163899Smjacob return (-1); 29279237Smjacob} 293163899Smjacob 294163899Smjacobint 295163899Smjacob_sem_unlink(const char *name) 296163899Smjacob{ 29779237Smjacob char path[PATH_MAX]; 29879237Smjacob 29935388Smjacob if (name[0] != '/') { 30035388Smjacob errno = ENOENT; 30135388Smjacob return -1; 30235388Smjacob } 303155704Smjacob name++; 304155704Smjacob strcpy(path, SEM_PREFIX); 30535388Smjacob if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 30635388Smjacob errno = ENAMETOOLONG; 30779237Smjacob return (-1); 308155704Smjacob } 309155704Smjacob 310155704Smjacob return (unlink(path)); 31179237Smjacob} 31279237Smjacob 31387635Smjacobint 31487635Smjacob_sem_destroy(sem_t *sem) 31579237Smjacob{ 316155704Smjacob 317155704Smjacob if (sem_check_validity(sem) != 0) 318155704Smjacob return (-1); 31979237Smjacob 32079237Smjacob if (sem->_kern._flags & SEM_NAMED) { 32179237Smjacob errno = EINVAL; 32235388Smjacob return (-1); 323155704Smjacob } 324155704Smjacob sem->_magic = 0; 325155704Smjacob return (0); 326155704Smjacob} 32745283Smjacob 32835388Smjacobint 32935388Smjacob_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 33035388Smjacob{ 33135388Smjacob 33235388Smjacob if (sem_check_validity(sem) != 0) 33335388Smjacob return (-1); 334196008Smjacob 335196008Smjacob *sval = (int)sem->_kern._count; 336196008Smjacob return (0); 33735388Smjacob} 33835388Smjacob 33939235Sgibbsstatic __inline int 34039235Sgibbsusem_wake(struct _usem *sem) 34139235Sgibbs{ 34239235Sgibbs return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 34339235Sgibbs} 34439235Sgibbs 34555363Smjacobstatic __inline int 34639235Sgibbsusem_wait(struct _usem *sem, const struct timespec *abstime) 34739235Sgibbs{ 34839235Sgibbs struct _umtx_time *tm_p, timeout; 34939235Sgibbs size_t tm_size; 35039235Sgibbs 35139235Sgibbs if (abstime == NULL) { 35239235Sgibbs tm_p = NULL; 35339235Sgibbs tm_size = 0; 35439235Sgibbs } else { 35539235Sgibbs timeout._clockid = CLOCK_REALTIME; 356163899Smjacob timeout._flags = UMTX_ABSTIME; 35779237Smjacob timeout._timeout = *abstime; 358163899Smjacob tm_p = &timeout; 35939235Sgibbs tm_size = sizeof(timeout); 36079237Smjacob } 36179237Smjacob return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, 362163899Smjacob (void *)tm_size, __DECONST(void*, tm_p)); 36339235Sgibbs} 36479237Smjacob 36579237Smjacobint 36679237Smjacob_sem_trywait(sem_t *sem) 36779237Smjacob{ 36879237Smjacob int val; 36979237Smjacob 37079237Smjacob if (sem_check_validity(sem) != 0) 371163899Smjacob return (-1); 372164370Smjacob 373196008Smjacob while ((val = sem->_kern._count) > 0) { 374196008Smjacob if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 375196008Smjacob return (0); 376163899Smjacob } 377163899Smjacob errno = EAGAIN; 378163899Smjacob return (-1); 379163899Smjacob} 380163899Smjacob 38135388Smjacobint 38239235Sgibbs_sem_timedwait(sem_t * __restrict sem, 38335388Smjacob const struct timespec * __restrict abstime) 38435388Smjacob{ 38535388Smjacob int val, retval; 386155704Smjacob 387155704Smjacob if (sem_check_validity(sem) != 0) 388155704Smjacob return (-1); 389155704Smjacob 390155704Smjacob retval = 0; 391155704Smjacob _pthread_testcancel(); 392155704Smjacob for (;;) { 393155704Smjacob while ((val = sem->_kern._count) > 0) { 394155704Smjacob if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 39535388Smjacob return (0); 39635388Smjacob } 397103825Smjacob 398103825Smjacob if (retval) { 399163899Smjacob _pthread_testcancel(); 400163899Smjacob break; 401163899Smjacob } 402163899Smjacob 403163899Smjacob /* 404163899Smjacob * The timeout argument is only supposed to 405163899Smjacob * be checked if the thread would have blocked. 406163899Smjacob */ 407163899Smjacob if (abstime != NULL) { 408163899Smjacob if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 409163899Smjacob errno = EINVAL; 410163899Smjacob return (-1); 411163899Smjacob } 412163899Smjacob } 413163899Smjacob _pthread_cancel_enter(1); 414163899Smjacob retval = usem_wait(&sem->_kern, abstime); 415163899Smjacob _pthread_cancel_leave(0); 416163899Smjacob } 417163899Smjacob return (retval); 418163899Smjacob} 419163899Smjacob 420163899Smjacobint 421163899Smjacob_sem_wait(sem_t *sem) 422163899Smjacob{ 423163899Smjacob return _sem_timedwait(sem, NULL); 42445283Smjacob} 42545283Smjacob 42645283Smjacob/* 427154704Smjacob * POSIX: 42845283Smjacob * The sem_post() interface is reentrant with respect to signals and may be 42982689Smjacob * invoked from a signal-catching function. 43035388Smjacob * The implementation does not use lock, so it should be safe. 43135388Smjacob */ 432155704Smjacobint 433155704Smjacob_sem_post(sem_t *sem) 434155704Smjacob{ 435155704Smjacob unsigned int count; 436155704Smjacob 437238869Smjacob if (sem_check_validity(sem) != 0) 438238869Smjacob return (-1); 439155704Smjacob 440155704Smjacob do { 441155704Smjacob count = sem->_kern._count; 442155704Smjacob if (count + 1 > SEM_VALUE_MAX) 44335388Smjacob return (EOVERFLOW); 44435388Smjacob } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1)); 44535388Smjacob (void)usem_wake(&sem->_kern); 446154704Smjacob return (0); 447154704Smjacob} 448155704Smjacob