sem_new.c revision 246872
1126596Sbms/* 2196155Ssam * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3126596Sbms * All rights reserved. 4126596Sbms * 5126596Sbms * Redistribution and use in source and binary forms, with or without 6126596Sbms * modification, are permitted provided that the following conditions 7126596Sbms * are met: 8126596Sbms * 1. Redistributions of source code must retain the above copyright 9126596Sbms * notice(s), this list of conditions and the following disclaimer as 10126596Sbms * the first lines of this file unmodified other than the possible 11126596Sbms * addition of one or more copyright notices. 12126596Sbms * 2. Redistributions in binary form must reproduce the above copyright 13126596Sbms * notice(s), this list of conditions and the following disclaimer in 14126596Sbms * the documentation and/or other materials provided with the 15126596Sbms * distribution. 16126596Sbms * 17126596Sbms * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18126596Sbms * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19126596Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20126596Sbms * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21126596Sbms * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22126596Sbms * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23126596Sbms * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24126596Sbms * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25126596Sbms * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26126596Sbms * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27126596Sbms * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28196155Ssam * 29131738Sru * $FreeBSD: head/lib/libc/gen/sem_new.c 246872 2013-02-16 06:07:07Z davidxu $ 30126596Sbms */ 31126596Sbms 32126596Sbms#include "namespace.h" 33196155Ssam#include <sys/types.h> 34126596Sbms#include <sys/queue.h> 35126596Sbms#include <sys/mman.h> 36196155Ssam#include <sys/stat.h> 37126596Sbms#include <errno.h> 38196155Ssam#include <machine/atomic.h> 39126596Sbms#include <sys/umtx.h> 40196155Ssam#include <limits.h> 41126596Sbms#include <fcntl.h> 42196155Ssam#include <pthread.h> 43126596Sbms#include <stdarg.h> 44196155Ssam#include <stdlib.h> 45196155Ssam#include <string.h> 46196155Ssam#include <time.h> 47126596Sbms#include <semaphore.h> 48196155Ssam#include <unistd.h> 49196155Ssam#include "un-namespace.h" 50196155Ssam#include "libc_private.h" 51196155Ssam 52126596Sbms__weak_reference(_sem_close, sem_close); 53196155Ssam__weak_reference(_sem_destroy, sem_destroy); 54196155Ssam__weak_reference(_sem_getvalue, sem_getvalue); 55196155Ssam__weak_reference(_sem_init, sem_init); 56196155Ssam__weak_reference(_sem_open, sem_open); 57196155Ssam__weak_reference(_sem_post, sem_post); 58196155Ssam__weak_reference(_sem_timedwait, sem_timedwait); 59196155Ssam__weak_reference(_sem_trywait, sem_trywait); 60196155Ssam__weak_reference(_sem_unlink, sem_unlink); 61196155Ssam__weak_reference(_sem_wait, sem_wait); 62196155Ssam 63196155Ssam#define SEM_PREFIX "/tmp/SEMD" 64196155Ssam#define SEM_MAGIC ((u_int32_t)0x73656d31) 65196155Ssam 66196155Ssamstruct sem_nameinfo { 67196155Ssam int open_count; 68196155Ssam char *name; 69196155Ssam sem_t *sem; 70196155Ssam LIST_ENTRY(sem_nameinfo) next; 71196155Ssam}; 72196155Ssam 73196155Ssamstatic pthread_once_t once = PTHREAD_ONCE_INIT; 74196155Ssamstatic pthread_mutex_t sem_llock; 75196155Ssamstatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 76196155Ssam 77196155Ssamstatic void 78196155Ssamsem_prefork() 79196155Ssam{ 80196155Ssam 81196155Ssam _pthread_mutex_lock(&sem_llock); 82196155Ssam} 83196155Ssam 84196155Ssamstatic void 85196155Ssamsem_postfork() 86196155Ssam{ 87196155Ssam _pthread_mutex_unlock(&sem_llock); 88196155Ssam} 89196155Ssam 90196155Ssamstatic void 91196155Ssamsem_child_postfork() 92196155Ssam{ 93196155Ssam _pthread_mutex_unlock(&sem_llock); 94196155Ssam} 95196155Ssam 96196155Ssamstatic void 97196155Ssamsem_module_init(void) 98196155Ssam{ 99196155Ssam pthread_mutexattr_t ma; 100196155Ssam 101196155Ssam _pthread_mutexattr_init(&ma); 102196155Ssam _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 103196155Ssam _pthread_mutex_init(&sem_llock, &ma); 104196155Ssam _pthread_mutexattr_destroy(&ma); 105196155Ssam _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 106196155Ssam} 107196155Ssam 108196155Ssamstatic inline int 109196155Ssamsem_check_validity(sem_t *sem) 110196155Ssam{ 111233648Seadler 112196155Ssam if (sem->_magic == SEM_MAGIC) 113196155Ssam return (0); 114196155Ssam else { 115196155Ssam errno = EINVAL; 116196155Ssam return (-1); 117196155Ssam } 118196155Ssam} 119196155Ssam 120196155Ssamint 121196155Ssam_sem_init(sem_t *sem, int pshared, unsigned int value) 122197300Sbrueffer{ 123196155Ssam 124196155Ssam if (value > SEM_VALUE_MAX) { 125196155Ssam errno = EINVAL; 126196155Ssam return (-1); 127196155Ssam } 128196155Ssam 129196155Ssam bzero(sem, sizeof(sem_t)); 130196155Ssam sem->_magic = SEM_MAGIC; 131196155Ssam sem->_kern._count = (u_int32_t)value; 132196155Ssam sem->_kern._has_waiters = 0; 133196155Ssam sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 134196155Ssam return (0); 135196155Ssam} 136196155Ssam 137196155Ssamsem_t * 138196155Ssam_sem_open(const char *name, int flags, ...) 139196155Ssam{ 140196155Ssam char path[PATH_MAX]; 141196155Ssam 142196155Ssam struct stat sb; 143196155Ssam va_list ap; 144196155Ssam struct sem_nameinfo *ni = NULL; 145196155Ssam sem_t *sem = NULL; 146233648Seadler int fd = -1, mode, len, errsave; 147196155Ssam int value = 0; 148196155Ssam 149196155Ssam if (name[0] != '/') { 150196155Ssam errno = EINVAL; 151196155Ssam return (SEM_FAILED); 152196155Ssam } 153233648Seadler name++; 154196155Ssam 155196155Ssam if (flags & ~(O_CREAT|O_EXCL)) { 156196155Ssam errno = EINVAL; 157196155Ssam return (SEM_FAILED); 158196155Ssam } 159196155Ssam 160196155Ssam _pthread_once(&once, sem_module_init); 161196155Ssam 162196155Ssam _pthread_mutex_lock(&sem_llock); 163196155Ssam LIST_FOREACH(ni, &sem_list, next) { 164233648Seadler if (strcmp(name, ni->name) == 0) { 165196155Ssam if ((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) { 166196155Ssam _pthread_mutex_unlock(&sem_llock); 167196155Ssam errno = EEXIST; 168196155Ssam return (SEM_FAILED); 169196155Ssam } else { 170196155Ssam ni->open_count++; 171196155Ssam sem = ni->sem; 172196155Ssam _pthread_mutex_unlock(&sem_llock); 173196155Ssam return (sem); 174196155Ssam } 175196155Ssam } 176196155Ssam } 177196155Ssam 178196155Ssam if (flags & O_CREAT) { 179196155Ssam va_start(ap, flags); 180196155Ssam mode = va_arg(ap, int); 181196155Ssam value = va_arg(ap, int); 182196155Ssam va_end(ap); 183196155Ssam } 184196155Ssam 185196155Ssam len = sizeof(*ni) + strlen(name) + 1; 186196155Ssam ni = (struct sem_nameinfo *)malloc(len); 187196155Ssam if (ni == NULL) { 188196155Ssam errno = ENOSPC; 189196155Ssam goto error; 190196155Ssam } 191196155Ssam 192196155Ssam ni->name = (char *)(ni+1); 193196155Ssam strcpy(ni->name, name); 194196155Ssam 195196155Ssam strcpy(path, SEM_PREFIX); 196196155Ssam if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 197196155Ssam errno = ENAMETOOLONG; 198196155Ssam goto error; 199196155Ssam } 200233648Seadler 201196155Ssam fd = _open(path, flags|O_RDWR|O_CLOEXEC|O_EXLOCK, mode); 202196155Ssam if (fd == -1) 203196155Ssam goto error; 204196155Ssam if (_fstat(fd, &sb)) 205233648Seadler goto error; 206196155Ssam if (sb.st_size < sizeof(sem_t)) { 207196155Ssam sem_t tmp; 208196155Ssam 209196155Ssam tmp._magic = SEM_MAGIC; 210196155Ssam tmp._kern._has_waiters = 0; 211196155Ssam tmp._kern._count = value; 212196155Ssam tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 213196155Ssam if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 214196155Ssam goto error; 215196155Ssam } 216196155Ssam flock(fd, LOCK_UN); 217196155Ssam sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 218196155Ssam MAP_SHARED|MAP_NOSYNC, fd, 0); 219196155Ssam if (sem == MAP_FAILED) { 220196155Ssam sem = NULL; 221196155Ssam if (errno == ENOMEM) 222196155Ssam errno = ENOSPC; 223196155Ssam goto error; 224196155Ssam } 225196155Ssam if (sem->_magic != SEM_MAGIC) { 226196155Ssam errno = EINVAL; 227196155Ssam goto error; 228196155Ssam } 229196155Ssam ni->open_count = 1; 230196155Ssam ni->sem = sem; 231126596Sbms LIST_INSERT_HEAD(&sem_list, ni, next); 232197300Sbrueffer _pthread_mutex_unlock(&sem_llock); 233197300Sbrueffer _close(fd); 234126596Sbms return (sem); 235197300Sbrueffer 236196155Ssamerror: 237126596Sbms errsave = errno; 238196155Ssam _pthread_mutex_unlock(&sem_llock); 239131738Sru if (fd != -1) 240196155Ssam _close(fd); 241235693Sgjb if (sem != NULL) 242 munmap(sem, sizeof(sem_t)); 243 free(ni); 244 errno = errsave; 245 return (SEM_FAILED); 246} 247 248int 249_sem_close(sem_t *sem) 250{ 251 struct sem_nameinfo *ni; 252 253 if (sem_check_validity(sem) != 0) 254 return (-1); 255 256 if (!(sem->_kern._flags & SEM_NAMED)) { 257 errno = EINVAL; 258 return (-1); 259 } 260 261 _pthread_once(&once, sem_module_init); 262 263 _pthread_mutex_lock(&sem_llock); 264 LIST_FOREACH(ni, &sem_list, next) { 265 if (sem == ni->sem) { 266 if (--ni->open_count > 0) { 267 _pthread_mutex_unlock(&sem_llock); 268 return (0); 269 } 270 else 271 break; 272 } 273 } 274 275 if (ni) { 276 LIST_REMOVE(ni, next); 277 _pthread_mutex_unlock(&sem_llock); 278 munmap(sem, sizeof(*sem)); 279 free(ni); 280 return (0); 281 } 282 _pthread_mutex_unlock(&sem_llock); 283 errno = EINVAL; 284 return (-1); 285} 286 287int 288_sem_unlink(const char *name) 289{ 290 char path[PATH_MAX]; 291 292 if (name[0] != '/') { 293 errno = ENOENT; 294 return -1; 295 } 296 name++; 297 298 strcpy(path, SEM_PREFIX); 299 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 300 errno = ENAMETOOLONG; 301 return (-1); 302 } 303 return unlink(path); 304} 305 306int 307_sem_destroy(sem_t *sem) 308{ 309 310 if (sem_check_validity(sem) != 0) 311 return (-1); 312 313 if (sem->_kern._flags & SEM_NAMED) { 314 errno = EINVAL; 315 return (-1); 316 } 317 sem->_magic = 0; 318 return (0); 319} 320 321int 322_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 323{ 324 325 if (sem_check_validity(sem) != 0) 326 return (-1); 327 328 *sval = (int)sem->_kern._count; 329 return (0); 330} 331 332static __inline int 333usem_wake(struct _usem *sem) 334{ 335 return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 336} 337 338static __inline int 339usem_wait(struct _usem *sem, const struct timespec *abstime) 340{ 341 struct _umtx_time *tm_p, timeout; 342 size_t tm_size; 343 344 if (abstime == NULL) { 345 tm_p = NULL; 346 tm_size = 0; 347 } else { 348 timeout._clockid = CLOCK_REALTIME; 349 timeout._flags = UMTX_ABSTIME; 350 timeout._timeout = *abstime; 351 tm_p = &timeout; 352 tm_size = sizeof(timeout); 353 } 354 return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, 355 (void *)tm_size, __DECONST(void*, tm_p)); 356} 357 358int 359_sem_trywait(sem_t *sem) 360{ 361 int val; 362 363 if (sem_check_validity(sem) != 0) 364 return (-1); 365 366 while ((val = sem->_kern._count) > 0) { 367 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 368 return (0); 369 } 370 errno = EAGAIN; 371 return (-1); 372} 373 374int 375_sem_timedwait(sem_t * __restrict sem, 376 const struct timespec * __restrict abstime) 377{ 378 int val, retval; 379 380 if (sem_check_validity(sem) != 0) 381 return (-1); 382 383 retval = 0; 384 for (;;) { 385 while ((val = sem->_kern._count) > 0) { 386 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 387 return (0); 388 } 389 390 if (retval) { 391 _pthread_testcancel(); 392 break; 393 } 394 395 /* 396 * The timeout argument is only supposed to 397 * be checked if the thread would have blocked. 398 */ 399 if (abstime != NULL) { 400 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 401 errno = EINVAL; 402 return (-1); 403 } 404 } 405 _pthread_cancel_enter(1); 406 retval = usem_wait(&sem->_kern, abstime); 407 _pthread_cancel_leave(0); 408 } 409 return (retval); 410} 411 412int 413_sem_wait(sem_t *sem) 414{ 415 return _sem_timedwait(sem, NULL); 416} 417 418/* 419 * POSIX: 420 * The sem_post() interface is reentrant with respect to signals and may be 421 * invoked from a signal-catching function. 422 * The implementation does not use lock, so it should be safe. 423 */ 424int 425_sem_post(sem_t *sem) 426{ 427 unsigned int count; 428 429 if (sem_check_validity(sem) != 0) 430 return (-1); 431 432 do { 433 count = sem->_kern._count; 434 if (count + 1 > SEM_VALUE_MAX) 435 return (EOVERFLOW); 436 } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1)); 437 (void)usem_wake(&sem->_kern); 438 return (0); 439} 440