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$ 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 dev_t dev; 70 ino_t ino; 71 sem_t *sem; 72 LIST_ENTRY(sem_nameinfo) next; 73}; 74 75static pthread_once_t once = PTHREAD_ONCE_INIT; 76static pthread_mutex_t sem_llock; 77static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 78 79static void 80sem_prefork() 81{ 82 83 _pthread_mutex_lock(&sem_llock); 84} 85 86static void 87sem_postfork() 88{ 89 _pthread_mutex_unlock(&sem_llock); 90} 91 92static void 93sem_child_postfork() 94{ 95 _pthread_mutex_unlock(&sem_llock); 96} 97 98static void 99sem_module_init(void) 100{ 101 pthread_mutexattr_t ma; 102 103 _pthread_mutexattr_init(&ma); 104 _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 105 _pthread_mutex_init(&sem_llock, &ma); 106 _pthread_mutexattr_destroy(&ma); 107 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 108} 109 110static inline int 111sem_check_validity(sem_t *sem) 112{ 113 114 if (sem->_magic == SEM_MAGIC) 115 return (0); 116 else { 117 errno = EINVAL; 118 return (-1); 119 } 120} 121 122int 123_sem_init(sem_t *sem, int pshared, unsigned int value) 124{ 125 126 if (value > SEM_VALUE_MAX) { 127 errno = EINVAL; 128 return (-1); 129 } 130 131 bzero(sem, sizeof(sem_t)); 132 sem->_magic = SEM_MAGIC; 133 sem->_kern._count = (u_int32_t)value; 134 sem->_kern._has_waiters = 0; 135 sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 136 return (0); 137} 138 139sem_t * 140_sem_open(const char *name, int flags, ...) 141{ 142 char path[PATH_MAX]; 143 144 struct stat sb; 145 va_list ap; 146 struct sem_nameinfo *ni = NULL; 147 sem_t *sem = NULL; 148 int fd = -1, mode, len, errsave; 149 int value = 0; 150 151 if (name[0] != '/') { 152 errno = EINVAL; 153 return (SEM_FAILED); 154 } 155 name++; 156 strcpy(path, SEM_PREFIX); 157 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 158 errno = ENAMETOOLONG; 159 return (SEM_FAILED); 160 } 161 if (flags & ~(O_CREAT|O_EXCL)) { 162 errno = EINVAL; 163 return (SEM_FAILED); 164 } 165 if ((flags & O_CREAT) != 0) { 166 va_start(ap, flags); 167 mode = va_arg(ap, int); 168 value = va_arg(ap, int); 169 va_end(ap); 170 } 171 fd = -1; 172 _pthread_once(&once, sem_module_init); 173 174 _pthread_mutex_lock(&sem_llock); 175 LIST_FOREACH(ni, &sem_list, next) { 176 if (ni->name != NULL && strcmp(name, ni->name) == 0) { 177 fd = _open(path, flags | O_RDWR | O_CLOEXEC | 178 O_EXLOCK, mode); 179 if (fd == -1 || _fstat(fd, &sb) == -1) 180 goto error; 181 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 182 O_EXCL) || ni->dev != sb.st_dev || 183 ni->ino != sb.st_ino) { 184 ni->name = NULL; 185 ni = NULL; 186 break; 187 } 188 ni->open_count++; 189 sem = ni->sem; 190 _pthread_mutex_unlock(&sem_llock); 191 _close(fd); 192 return (sem); 193 } 194 } 195 196 len = sizeof(*ni) + strlen(name) + 1; 197 ni = (struct sem_nameinfo *)malloc(len); 198 if (ni == NULL) { 199 errno = ENOSPC; 200 goto error; 201 } 202 203 ni->name = (char *)(ni+1); 204 strcpy(ni->name, name); 205 206 if (fd == -1) { 207 fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 208 if (fd == -1 || _fstat(fd, &sb) == -1) 209 goto error; 210 } 211 if (sb.st_size < sizeof(sem_t)) { 212 sem_t tmp; 213 214 tmp._magic = SEM_MAGIC; 215 tmp._kern._has_waiters = 0; 216 tmp._kern._count = value; 217 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 218 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 219 goto error; 220 } 221 flock(fd, LOCK_UN); 222 sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 223 MAP_SHARED|MAP_NOSYNC, fd, 0); 224 if (sem == MAP_FAILED) { 225 sem = NULL; 226 if (errno == ENOMEM) 227 errno = ENOSPC; 228 goto error; 229 } 230 if (sem->_magic != SEM_MAGIC) { 231 errno = EINVAL; 232 goto error; 233 } 234 ni->open_count = 1; 235 ni->sem = sem; 236 ni->dev = sb.st_dev; 237 ni->ino = sb.st_ino; 238 LIST_INSERT_HEAD(&sem_list, ni, next); 239 _close(fd); 240 _pthread_mutex_unlock(&sem_llock); 241 return (sem); 242 243error: 244 errsave = errno; 245 if (fd != -1) 246 _close(fd); 247 if (sem != NULL) 248 munmap(sem, sizeof(sem_t)); 249 free(ni); 250 _pthread_mutex_unlock(&sem_llock); 251 errno = errsave; 252 return (SEM_FAILED); 253} 254 255int 256_sem_close(sem_t *sem) 257{ 258 struct sem_nameinfo *ni; 259 260 if (sem_check_validity(sem) != 0) 261 return (-1); 262 263 if (!(sem->_kern._flags & SEM_NAMED)) { 264 errno = EINVAL; 265 return (-1); 266 } 267 268 _pthread_once(&once, sem_module_init); 269 270 _pthread_mutex_lock(&sem_llock); 271 LIST_FOREACH(ni, &sem_list, next) { 272 if (sem == ni->sem) { 273 if (--ni->open_count > 0) { 274 _pthread_mutex_unlock(&sem_llock); 275 return (0); 276 } 277 else 278 break; 279 } 280 } 281 282 if (ni) { 283 LIST_REMOVE(ni, next); 284 _pthread_mutex_unlock(&sem_llock); 285 munmap(sem, sizeof(*sem)); 286 free(ni); 287 return (0); 288 } 289 _pthread_mutex_unlock(&sem_llock); 290 errno = EINVAL; 291 return (-1); 292} 293 294int 295_sem_unlink(const char *name) 296{ 297 char path[PATH_MAX]; 298 299 if (name[0] != '/') { 300 errno = ENOENT; 301 return -1; 302 } 303 name++; 304 strcpy(path, SEM_PREFIX); 305 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 306 errno = ENAMETOOLONG; 307 return (-1); 308 } 309 310 return (unlink(path)); 311} 312 313int 314_sem_destroy(sem_t *sem) 315{ 316 317 if (sem_check_validity(sem) != 0) 318 return (-1); 319 320 if (sem->_kern._flags & SEM_NAMED) { 321 errno = EINVAL; 322 return (-1); 323 } 324 sem->_magic = 0; 325 return (0); 326} 327 328int 329_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 330{ 331 332 if (sem_check_validity(sem) != 0) 333 return (-1); 334 335 *sval = (int)sem->_kern._count; 336 return (0); 337} 338 339static __inline int 340usem_wake(struct _usem *sem) 341{ 342 if (!sem->_has_waiters) 343 return (0); 344 return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 345} 346 347static __inline int 348usem_wait(struct _usem *sem, const struct timespec *timeout) 349{ 350 if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 351 timeout->tv_nsec <= 0))) { 352 errno = ETIMEDOUT; 353 return (-1); 354 } 355 return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, 356 __DECONST(void*, timeout)); 357} 358 359int 360_sem_trywait(sem_t *sem) 361{ 362 int val; 363 364 if (sem_check_validity(sem) != 0) 365 return (-1); 366 367 while ((val = sem->_kern._count) > 0) { 368 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 369 return (0); 370 } 371 errno = EAGAIN; 372 return (-1); 373} 374 375#define TIMESPEC_SUB(dst, src, val) \ 376 do { \ 377 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 378 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 379 if ((dst)->tv_nsec < 0) { \ 380 (dst)->tv_sec--; \ 381 (dst)->tv_nsec += 1000000000; \ 382 } \ 383 } while (0) 384 385 386int 387_sem_timedwait(sem_t * __restrict sem, 388 const struct timespec * __restrict abstime) 389{ 390 struct timespec ts, ts2; 391 int val, retval; 392 393 if (sem_check_validity(sem) != 0) 394 return (-1); 395 396 retval = 0; 397 _pthread_testcancel(); 398 for (;;) { 399 while ((val = sem->_kern._count) > 0) { 400 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 401 return (0); 402 } 403 404 if (retval) { 405 _pthread_testcancel(); 406 break; 407 } 408 409 /* 410 * The timeout argument is only supposed to 411 * be checked if the thread would have blocked. 412 */ 413 if (abstime != NULL) { 414 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 415 errno = EINVAL; 416 return (-1); 417 } 418 clock_gettime(CLOCK_REALTIME, &ts); 419 TIMESPEC_SUB(&ts2, abstime, &ts); 420 } 421 _pthread_cancel_enter(1); 422 retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); 423 _pthread_cancel_leave(0); 424 } 425 return (retval); 426} 427 428int 429_sem_wait(sem_t *sem) 430{ 431 return _sem_timedwait(sem, NULL); 432} 433 434/* 435 * POSIX: 436 * The sem_post() interface is reentrant with respect to signals and may be 437 * invoked from a signal-catching function. 438 * The implementation does not use lock, so it should be safe. 439 */ 440int 441_sem_post(sem_t *sem) 442{ 443 444 if (sem_check_validity(sem) != 0) 445 return (-1); 446 447 atomic_add_rel_int(&sem->_kern._count, 1); 448 return usem_wake(&sem->_kern); 449} 450