sem_new.c revision 202326
1222656Sed/* 2222656Sed * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3214152Sed * All rights reserved. 4214152Sed * 5214152Sed * Redistribution and use in source and binary forms, with or without 6214152Sed * modification, are permitted provided that the following conditions 7214152Sed * are met: 8214152Sed * 1. Redistributions of source code must retain the above copyright 9214152Sed * notice(s), this list of conditions and the following disclaimer as 10214152Sed * the first lines of this file unmodified other than the possible 11214152Sed * addition of one or more copyright notices. 12214152Sed * 2. Redistributions in binary form must reproduce the above copyright 13214152Sed * notice(s), this list of conditions and the following disclaimer in 14214152Sed * the documentation and/or other materials provided with the 15214152Sed * distribution. 16214152Sed * 17214152Sed * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18214152Sed * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19214152Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20214152Sed * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21214152Sed * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22214152Sed * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23214152Sed * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24214152Sed * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25214152Sed * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26214152Sed * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27214152Sed * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28214152Sed * 29214152Sed * $FreeBSD: head/lib/libc/gen/sem_new.c 202326 2010-01-15 01:19:58Z davidxu $ 30214152Sed */ 31214152Sed 32214152Sed#include "namespace.h" 33214152Sed#include <sys/types.h> 34214152Sed#include <sys/queue.h> 35214152Sed#include <sys/mman.h> 36214152Sed#include <sys/stat.h> 37214152Sed#include <errno.h> 38214152Sed#include <machine/atomic.h> 39214152Sed#include <sys/umtx.h> 40214152Sed#include <limits.h> 41214152Sed#include <fcntl.h> 42214152Sed#include <pthread.h> 43214152Sed#include <stdarg.h> 44214152Sed#include <stdlib.h> 45214152Sed#include <string.h> 46214152Sed#include <time.h> 47214152Sed#include <semaphore.h> 48214152Sed#include <unistd.h> 49214152Sed#include "un-namespace.h" 50214152Sed 51214152Sed__weak_reference(_sem_close, sem_close); 52214152Sed__weak_reference(_sem_destroy, sem_destroy); 53214152Sed__weak_reference(_sem_getvalue, sem_getvalue); 54214152Sed__weak_reference(_sem_init, sem_init); 55214152Sed__weak_reference(_sem_open, sem_open); 56214152Sed__weak_reference(_sem_post, sem_post); 57214152Sed__weak_reference(_sem_timedwait, sem_timedwait); 58214152Sed__weak_reference(_sem_trywait, sem_trywait); 59214152Sed__weak_reference(_sem_unlink, sem_unlink); 60214152Sed__weak_reference(_sem_wait, sem_wait); 61214152Sed 62214152Sed#define SEM_PREFIX "/tmp/SEMD" 63214152Sed#define SEM_MAGIC ((u_int32_t)0x73656d31) 64214152Sed 65214152Sedstruct sem_nameinfo { 66214152Sed int open_count; 67214152Sed char *name; 68214152Sed sem_t *sem; 69214152Sed LIST_ENTRY(sem_nameinfo) next; 70214152Sed}; 71214152Sed 72214152Sedstatic pthread_once_t once = PTHREAD_ONCE_INIT; 73214152Sedstatic pthread_mutex_t sem_llock; 74214152Sedstatic LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 75214152Sed 76214152Sedstatic void 77214152Sedsem_prefork() 78214152Sed{ 79214152Sed 80214152Sed _pthread_mutex_lock(&sem_llock); 81214152Sed} 82214152Sed 83214152Sedstatic void 84214152Sedsem_postfork() 85214152Sed{ 86214152Sed _pthread_mutex_unlock(&sem_llock); 87214152Sed} 88214152Sed 89214152Sedstatic void 90214152Sedsem_child_postfork() 91214152Sed{ 92214152Sed _pthread_mutex_unlock(&sem_llock); 93214152Sed} 94214152Sed 95214152Sedstatic void 96214152Sedsem_module_init(void) 97214152Sed{ 98214152Sed pthread_mutexattr_t ma; 99214152Sed 100214152Sed _pthread_mutexattr_init(&ma); 101214152Sed _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 102214152Sed _pthread_mutex_init(&sem_llock, &ma); 103214152Sed _pthread_mutexattr_destroy(&ma); 104214152Sed _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 105214152Sed} 106214152Sed 107214152Sedstatic inline int 108214152Sedsem_check_validity(sem_t *sem) 109214152Sed{ 110214152Sed 111214152Sed if (sem->_magic == SEM_MAGIC) 112214152Sed return (0); 113214152Sed else { 114214152Sed errno = EINVAL; 115 return (-1); 116 } 117} 118 119int 120_sem_init(sem_t *sem, int pshared, unsigned int value) 121{ 122 123 if (value > SEM_VALUE_MAX) { 124 errno = EINVAL; 125 return (-1); 126 } 127 128 bzero(sem, sizeof(sem_t)); 129 sem->_magic = SEM_MAGIC; 130 sem->_kern._count = (u_int32_t)value; 131 sem->_kern._has_waiters = 0; 132 sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 133 return (0); 134} 135 136sem_t * 137_sem_open(const char *name, int flags, ...) 138{ 139 char path[PATH_MAX]; 140 141 struct stat sb; 142 va_list ap; 143 struct sem_nameinfo *ni = NULL; 144 sem_t *sem = NULL; 145 int fd = -1, mode, len; 146 int value = 0; 147 148 if (name[0] != '/') { 149 errno = EINVAL; 150 return (SEM_FAILED); 151 } 152 name++; 153 154 if (flags & ~(O_CREAT|O_EXCL)) { 155 errno = EINVAL; 156 return (SEM_FAILED); 157 } 158 159 _pthread_once(&once, sem_module_init); 160 161 _pthread_mutex_lock(&sem_llock); 162 LIST_FOREACH(ni, &sem_list, next) { 163 if (strcmp(name, ni->name) == 0) { 164 ni->open_count++; 165 sem = ni->sem; 166 _pthread_mutex_unlock(&sem_llock); 167 return (sem); 168 } 169 } 170 171 if (flags & O_CREAT) { 172 va_start(ap, flags); 173 mode = va_arg(ap, int); 174 value = va_arg(ap, int); 175 va_end(ap); 176 } 177 178 len = sizeof(*ni) + strlen(name) + 1; 179 ni = (struct sem_nameinfo *)malloc(len); 180 if (ni == NULL) { 181 errno = ENOSPC; 182 goto error; 183 } 184 185 ni->name = (char *)(ni+1); 186 strcpy(ni->name, name); 187 188 strcpy(path, SEM_PREFIX); 189 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 190 errno = ENAMETOOLONG; 191 goto error; 192 } 193 194 fd = _open(path, flags|O_RDWR, mode); 195 if (fd == -1) 196 goto error; 197 if (flock(fd, LOCK_EX) == -1) 198 goto error; 199 if (_fstat(fd, &sb)) { 200 flock(fd, LOCK_UN); 201 goto error; 202 } 203 if (sb.st_size < sizeof(sem_t)) { 204 sem_t tmp; 205 206 tmp._magic = SEM_MAGIC; 207 tmp._kern._has_waiters = 0; 208 tmp._kern._count = value; 209 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 210 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { 211 flock(fd, LOCK_UN); 212 goto error; 213 } 214 } 215 flock(fd, LOCK_UN); 216 sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 217 MAP_SHARED|MAP_NOSYNC, fd, 0); 218 if (sem == MAP_FAILED) { 219 sem = NULL; 220 if (errno == ENOMEM) 221 errno = ENOSPC; 222 goto error; 223 } 224 if (sem->_magic != SEM_MAGIC) { 225 errno = EINVAL; 226 goto error; 227 } 228 ni->open_count = 1; 229 ni->sem = sem; 230 LIST_INSERT_HEAD(&sem_list, ni, next); 231 _pthread_mutex_unlock(&sem_llock); 232 _close(fd); 233 return (sem); 234 235error: 236 _pthread_mutex_unlock(&sem_llock); 237 if (fd != -1) 238 _close(fd); 239 if (sem != NULL) 240 munmap(sem, sizeof(sem_t)); 241 free(ni); 242 return (SEM_FAILED); 243} 244 245int 246_sem_close(sem_t *sem) 247{ 248 struct sem_nameinfo *ni; 249 250 if (sem_check_validity(sem) != 0) 251 return (-1); 252 253 if (!(sem->_kern._flags & SEM_NAMED)) { 254 errno = EINVAL; 255 return (-1); 256 } 257 258 _pthread_once(&once, sem_module_init); 259 260 _pthread_mutex_lock(&sem_llock); 261 LIST_FOREACH(ni, &sem_list, next) { 262 if (sem == ni->sem) { 263 if (--ni->open_count > 0) { 264 _pthread_mutex_unlock(&sem_llock); 265 return (0); 266 } 267 else 268 break; 269 } 270 } 271 272 if (ni) { 273 LIST_REMOVE(ni, next); 274 _pthread_mutex_unlock(&sem_llock); 275 munmap(sem, sizeof(*sem)); 276 free(ni); 277 return (0); 278 } 279 _pthread_mutex_unlock(&sem_llock); 280 errno = EINVAL; 281 return (-1); 282} 283 284int 285_sem_unlink(const char *name) 286{ 287 char path[PATH_MAX]; 288 289 if (name[0] != '/') { 290 errno = ENOENT; 291 return -1; 292 } 293 name++; 294 295 strcpy(path, SEM_PREFIX); 296 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 297 errno = ENAMETOOLONG; 298 return (-1); 299 } 300 return unlink(path); 301} 302 303int 304_sem_destroy(sem_t *sem) 305{ 306 307 if (sem_check_validity(sem) != 0) 308 return (-1); 309 310 if (sem->_kern._flags & SEM_NAMED) { 311 errno = EINVAL; 312 return (-1); 313 } 314 sem->_magic = 0; 315 return (0); 316} 317 318int 319_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 320{ 321 322 if (sem_check_validity(sem) != 0) 323 return (-1); 324 325 *sval = (int)sem->_kern._count; 326 return (0); 327} 328 329static __inline int 330usem_wake(struct _usem *sem) 331{ 332 if (!sem->_has_waiters) 333 return (0); 334 return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 335} 336 337static __inline int 338usem_wait(struct _usem *sem, const struct timespec *timeout) 339{ 340 if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 341 timeout->tv_nsec <= 0))) { 342 errno = ETIMEDOUT; 343 return (-1); 344 } 345 return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, 346 __DECONST(void*, timeout)); 347} 348 349int 350_sem_trywait(sem_t *sem) 351{ 352 int val; 353 354 if (sem_check_validity(sem) != 0) 355 return (-1); 356 357 while ((val = sem->_kern._count) > 0) { 358 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 359 return (0); 360 } 361 errno = EAGAIN; 362 return (-1); 363} 364 365static void 366sem_cancel_handler(void *arg) 367{ 368 sem_t *sem = arg; 369 370 if (sem->_kern._has_waiters && sem->_kern._count) 371 usem_wake(&sem->_kern); 372} 373 374#define TIMESPEC_SUB(dst, src, val) \ 375 do { \ 376 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 377 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 378 if ((dst)->tv_nsec < 0) { \ 379 (dst)->tv_sec--; \ 380 (dst)->tv_nsec += 1000000000; \ 381 } \ 382 } while (0) 383 384 385static __inline int 386enable_async_cancel(void) 387{ 388 int old; 389 390 _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); 391 return (old); 392} 393 394static __inline void 395restore_async_cancel(int val) 396{ 397 _pthread_setcanceltype(val, NULL); 398} 399 400int 401_sem_timedwait(sem_t * __restrict sem, 402 const struct timespec * __restrict abstime) 403{ 404 struct timespec ts, ts2; 405 int val, retval, saved_cancel; 406 407 if (sem_check_validity(sem) != 0) 408 return (-1); 409 410 retval = 0; 411 for (;;) { 412 while ((val = sem->_kern._count) > 0) { 413 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 414 return (0); 415 } 416 417 if (retval) 418 break; 419 420 /* 421 * The timeout argument is only supposed to 422 * be checked if the thread would have blocked. 423 */ 424 if (abstime != NULL) { 425 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 426 errno = EINVAL; 427 return (-1); 428 } 429 clock_gettime(CLOCK_REALTIME, &ts); 430 TIMESPEC_SUB(&ts2, abstime, &ts); 431 } 432 pthread_cleanup_push(sem_cancel_handler, sem); 433 saved_cancel = enable_async_cancel(); 434 retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); 435 restore_async_cancel(saved_cancel); 436 pthread_cleanup_pop(0); 437 } 438 return (retval); 439} 440 441int 442_sem_wait(sem_t *sem) 443{ 444 return _sem_timedwait(sem, NULL); 445} 446 447/* 448 * POSIX: 449 * The sem_post() interface is reentrant with respect to signals and may be 450 * invoked from a signal-catching function. 451 * The implementation does not use lock, so it should be safe. 452 */ 453int 454_sem_post(sem_t *sem) 455{ 456 457 if (sem_check_validity(sem) != 0) 458 return (-1); 459 460 atomic_add_rel_int(&sem->_kern._count, 1); 461 return usem_wake(&sem->_kern); 462} 463