sem_new.c revision 234057
1/* 2 * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice(s), this list of conditions and the following disclaimer as 10 * the first lines of this file unmodified other than the possible 11 * addition of one or more copyright notices. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice(s), this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD: head/lib/libc/gen/sem_new.c 234057 2012-04-09 14:17:22Z jilles $ 30 */ 31 32#include "namespace.h" 33#include <sys/types.h> 34#include <sys/queue.h> 35#include <sys/mman.h> 36#include <sys/stat.h> 37#include <errno.h> 38#include <machine/atomic.h> 39#include <sys/umtx.h> 40#include <limits.h> 41#include <fcntl.h> 42#include <pthread.h> 43#include <stdarg.h> 44#include <stdlib.h> 45#include <string.h> 46#include <time.h> 47#include <semaphore.h> 48#include <unistd.h> 49#include "un-namespace.h" 50#include "libc_private.h" 51 52__weak_reference(_sem_close, sem_close); 53__weak_reference(_sem_destroy, sem_destroy); 54__weak_reference(_sem_getvalue, sem_getvalue); 55__weak_reference(_sem_init, sem_init); 56__weak_reference(_sem_open, sem_open); 57__weak_reference(_sem_post, sem_post); 58__weak_reference(_sem_timedwait, sem_timedwait); 59__weak_reference(_sem_trywait, sem_trywait); 60__weak_reference(_sem_unlink, sem_unlink); 61__weak_reference(_sem_wait, sem_wait); 62 63#define SEM_PREFIX "/tmp/SEMD" 64#define SEM_MAGIC ((u_int32_t)0x73656d31) 65 66struct sem_nameinfo { 67 int open_count; 68 char *name; 69 sem_t *sem; 70 LIST_ENTRY(sem_nameinfo) next; 71}; 72 73static pthread_once_t once = PTHREAD_ONCE_INIT; 74static pthread_mutex_t sem_llock; 75static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 76 77static void 78sem_prefork() 79{ 80 81 _pthread_mutex_lock(&sem_llock); 82} 83 84static void 85sem_postfork() 86{ 87 _pthread_mutex_unlock(&sem_llock); 88} 89 90static void 91sem_child_postfork() 92{ 93 _pthread_mutex_unlock(&sem_llock); 94} 95 96static void 97sem_module_init(void) 98{ 99 pthread_mutexattr_t ma; 100 101 _pthread_mutexattr_init(&ma); 102 _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 103 _pthread_mutex_init(&sem_llock, &ma); 104 _pthread_mutexattr_destroy(&ma); 105 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 106} 107 108static inline int 109sem_check_validity(sem_t *sem) 110{ 111 112 if (sem->_magic == SEM_MAGIC) 113 return (0); 114 else { 115 errno = EINVAL; 116 return (-1); 117 } 118} 119 120int 121_sem_init(sem_t *sem, int pshared, unsigned int value) 122{ 123 124 if (value > SEM_VALUE_MAX) { 125 errno = EINVAL; 126 return (-1); 127 } 128 129 bzero(sem, sizeof(sem_t)); 130 sem->_magic = SEM_MAGIC; 131 sem->_kern._count = (u_int32_t)value; 132 sem->_kern._has_waiters = 0; 133 sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 134 return (0); 135} 136 137sem_t * 138_sem_open(const char *name, int flags, ...) 139{ 140 char path[PATH_MAX]; 141 142 struct stat sb; 143 va_list ap; 144 struct sem_nameinfo *ni = NULL; 145 sem_t *sem = NULL; 146 int fd = -1, mode, len, errsave; 147 int value = 0; 148 149 if (name[0] != '/') { 150 errno = EINVAL; 151 return (SEM_FAILED); 152 } 153 name++; 154 155 if (flags & ~(O_CREAT|O_EXCL)) { 156 errno = EINVAL; 157 return (SEM_FAILED); 158 } 159 160 _pthread_once(&once, sem_module_init); 161 162 _pthread_mutex_lock(&sem_llock); 163 LIST_FOREACH(ni, &sem_list, next) { 164 if (strcmp(name, ni->name) == 0) { 165 if ((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) { 166 _pthread_mutex_unlock(&sem_llock); 167 errno = EEXIST; 168 return (SEM_FAILED); 169 } else { 170 ni->open_count++; 171 sem = ni->sem; 172 _pthread_mutex_unlock(&sem_llock); 173 return (sem); 174 } 175 } 176 } 177 178 if (flags & O_CREAT) { 179 va_start(ap, flags); 180 mode = va_arg(ap, int); 181 value = va_arg(ap, int); 182 va_end(ap); 183 } 184 185 len = sizeof(*ni) + strlen(name) + 1; 186 ni = (struct sem_nameinfo *)malloc(len); 187 if (ni == NULL) { 188 errno = ENOSPC; 189 goto error; 190 } 191 192 ni->name = (char *)(ni+1); 193 strcpy(ni->name, name); 194 195 strcpy(path, SEM_PREFIX); 196 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 197 errno = ENAMETOOLONG; 198 goto error; 199 } 200 201 fd = _open(path, flags|O_RDWR, mode); 202 if (fd == -1) 203 goto error; 204 if (flock(fd, LOCK_EX) == -1) 205 goto error; 206 if (_fstat(fd, &sb)) { 207 flock(fd, LOCK_UN); 208 goto error; 209 } 210 if (sb.st_size < sizeof(sem_t)) { 211 sem_t tmp; 212 213 tmp._magic = SEM_MAGIC; 214 tmp._kern._has_waiters = 0; 215 tmp._kern._count = value; 216 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 217 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { 218 flock(fd, LOCK_UN); 219 goto error; 220 } 221 } 222 flock(fd, LOCK_UN); 223 sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 224 MAP_SHARED|MAP_NOSYNC, fd, 0); 225 if (sem == MAP_FAILED) { 226 sem = NULL; 227 if (errno == ENOMEM) 228 errno = ENOSPC; 229 goto error; 230 } 231 if (sem->_magic != SEM_MAGIC) { 232 errno = EINVAL; 233 goto error; 234 } 235 ni->open_count = 1; 236 ni->sem = sem; 237 LIST_INSERT_HEAD(&sem_list, ni, next); 238 _pthread_mutex_unlock(&sem_llock); 239 _close(fd); 240 return (sem); 241 242error: 243 errsave = errno; 244 _pthread_mutex_unlock(&sem_llock); 245 if (fd != -1) 246 _close(fd); 247 if (sem != NULL) 248 munmap(sem, sizeof(sem_t)); 249 free(ni); 250 errno = errsave; 251 return (SEM_FAILED); 252} 253 254int 255_sem_close(sem_t *sem) 256{ 257 struct sem_nameinfo *ni; 258 259 if (sem_check_validity(sem) != 0) 260 return (-1); 261 262 if (!(sem->_kern._flags & SEM_NAMED)) { 263 errno = EINVAL; 264 return (-1); 265 } 266 267 _pthread_once(&once, sem_module_init); 268 269 _pthread_mutex_lock(&sem_llock); 270 LIST_FOREACH(ni, &sem_list, next) { 271 if (sem == ni->sem) { 272 if (--ni->open_count > 0) { 273 _pthread_mutex_unlock(&sem_llock); 274 return (0); 275 } 276 else 277 break; 278 } 279 } 280 281 if (ni) { 282 LIST_REMOVE(ni, next); 283 _pthread_mutex_unlock(&sem_llock); 284 munmap(sem, sizeof(*sem)); 285 free(ni); 286 return (0); 287 } 288 _pthread_mutex_unlock(&sem_llock); 289 errno = EINVAL; 290 return (-1); 291} 292 293int 294_sem_unlink(const char *name) 295{ 296 char path[PATH_MAX]; 297 298 if (name[0] != '/') { 299 errno = ENOENT; 300 return -1; 301 } 302 name++; 303 304 strcpy(path, SEM_PREFIX); 305 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 306 errno = ENAMETOOLONG; 307 return (-1); 308 } 309 return unlink(path); 310} 311 312int 313_sem_destroy(sem_t *sem) 314{ 315 316 if (sem_check_validity(sem) != 0) 317 return (-1); 318 319 if (sem->_kern._flags & SEM_NAMED) { 320 errno = EINVAL; 321 return (-1); 322 } 323 sem->_magic = 0; 324 return (0); 325} 326 327int 328_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 329{ 330 331 if (sem_check_validity(sem) != 0) 332 return (-1); 333 334 *sval = (int)sem->_kern._count; 335 return (0); 336} 337 338static __inline int 339usem_wake(struct _usem *sem) 340{ 341 return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 342} 343 344static __inline int 345usem_wait(struct _usem *sem, const struct timespec *abstime) 346{ 347 struct _umtx_time *tm_p, timeout; 348 size_t tm_size; 349 350 if (abstime == NULL) { 351 tm_p = NULL; 352 tm_size = 0; 353 } else { 354 timeout._clockid = CLOCK_REALTIME; 355 timeout._flags = UMTX_ABSTIME; 356 timeout._timeout = *abstime; 357 tm_p = &timeout; 358 tm_size = sizeof(timeout); 359 } 360 return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, 361 (void *)tm_size, __DECONST(void*, tm_p)); 362} 363 364int 365_sem_trywait(sem_t *sem) 366{ 367 int val; 368 369 if (sem_check_validity(sem) != 0) 370 return (-1); 371 372 while ((val = sem->_kern._count) > 0) { 373 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 374 return (0); 375 } 376 errno = EAGAIN; 377 return (-1); 378} 379 380int 381_sem_timedwait(sem_t * __restrict sem, 382 const struct timespec * __restrict abstime) 383{ 384 int val, retval; 385 386 if (sem_check_validity(sem) != 0) 387 return (-1); 388 389 retval = 0; 390 for (;;) { 391 while ((val = sem->_kern._count) > 0) { 392 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 393 return (0); 394 } 395 396 if (retval) { 397 _pthread_testcancel(); 398 break; 399 } 400 401 /* 402 * The timeout argument is only supposed to 403 * be checked if the thread would have blocked. 404 */ 405 if (abstime != NULL) { 406 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 407 errno = EINVAL; 408 return (-1); 409 } 410 } 411 _pthread_cancel_enter(1); 412 retval = usem_wait(&sem->_kern, abstime); 413 _pthread_cancel_leave(0); 414 } 415 return (retval); 416} 417 418int 419_sem_wait(sem_t *sem) 420{ 421 return _sem_timedwait(sem, NULL); 422} 423 424/* 425 * POSIX: 426 * The sem_post() interface is reentrant with respect to signals and may be 427 * invoked from a signal-catching function. 428 * The implementation does not use lock, so it should be safe. 429 */ 430int 431_sem_post(sem_t *sem) 432{ 433 unsigned int count; 434 435 if (sem_check_validity(sem) != 0) 436 return (-1); 437 438 do { 439 count = sem->_kern._count; 440 if (count + 1 > SEM_VALUE_MAX) 441 return (EOVERFLOW); 442 } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1)); 443 (void)usem_wake(&sem->_kern); 444 return (0); 445} 446