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 66 struct sem_nameinfo { 67 int open_count; 68 char *name; 69 sem_t *sem; 70 LIST_ENTRY(sem_nameinfo) next; 71 }; 72 73 static pthread_once_t once = PTHREAD_ONCE_INIT; 74 static pthread_mutex_t sem_llock; 75 static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 76 77 static void 78 sem_prefork() 79 { 80 81 _pthread_mutex_lock(&sem_llock); 82 } 83 84 static void 85 sem_postfork() 86 { 87 _pthread_mutex_unlock(&sem_llock); 88 } 89 90 static void 91 sem_child_postfork() 92 { 93 _pthread_mutex_unlock(&sem_llock); 94 } 95 96 static void 97 sem_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 108 static inline int 109 sem_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 120 int 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 137 sem_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|O_CLOEXEC|O_EXLOCK, mode); 202 if (fd == -1) 203 goto error; 204 if (_fstat(fd, &sb)) 205 goto error; 206 if (sb.st_size < sizeof(sem_t)) { 207 sem_t tmp; 208 209 tmp._magic = SEM_MAGIC; 210 tmp._kern._has_waiters = 0; 211 tmp._kern._count = value; 212 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 213 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 214 goto error; 215 } 216 flock(fd, LOCK_UN); 217 sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 218 MAP_SHARED|MAP_NOSYNC, fd, 0); 219 if (sem == MAP_FAILED) { 220 sem = NULL; 221 if (errno == ENOMEM) 222 errno = ENOSPC; 223 goto error; 224 } 225 if (sem->_magic != SEM_MAGIC) { 226 errno = EINVAL; 227 goto error; 228 } 229 ni->open_count = 1; 230 ni->sem = sem; 231 LIST_INSERT_HEAD(&sem_list, ni, next); 232 _close(fd); 233 _pthread_mutex_unlock(&sem_llock); 234 return (sem); 235 236 error: 237 errsave = errno; 238 if (fd != -1) 239 _close(fd); 240 if (sem != NULL) 241 munmap(sem, sizeof(sem_t)); 242 free(ni); 243 _pthread_mutex_unlock(&sem_llock); 244 errno = errsave; 245 return (SEM_FAILED); 246 } 247 248 int 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 287 int 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 306 int 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 321 int 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 332 static __inline int 333 usem_wake(struct _usem *sem) 334 { 335 return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 336 } 337 338 static __inline int 339 usem_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 358 int 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 374 int 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 _pthread_testcancel(); 385 for (;;) { 386 while ((val = sem->_kern._count) > 0) { 387 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 388 return (0); 389 } 390 391 if (retval) { 392 _pthread_testcancel(); 393 break; 394 } 395 396 /* 397 * The timeout argument is only supposed to 398 * be checked if the thread would have blocked. 399 */ 400 if (abstime != NULL) { 401 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 402 errno = EINVAL; 403 return (-1); 404 } 405 } 406 _pthread_cancel_enter(1); 407 retval = usem_wait(&sem->_kern, abstime); 408 _pthread_cancel_leave(0); 409 } 410 return (retval); 411 } 412 413 int 414 _sem_wait(sem_t *sem) 415 { 416 return _sem_timedwait(sem, NULL); 417 } 418 419 /* 420 * POSIX: 421 * The sem_post() interface is reentrant with respect to signals and may be 422 * invoked from a signal-catching function. 423 * The implementation does not use lock, so it should be safe. 424 */ 425 int 426 _sem_post(sem_t *sem) 427 { 428 unsigned int count; 429 430 if (sem_check_validity(sem) != 0) 431 return (-1); 432 433 do { 434 count = sem->_kern._count; 435 if (count + 1 > SEM_VALUE_MAX) 436 return (EOVERFLOW); 437 } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1)); 438 (void)usem_wake(&sem->_kern); 439 return (0); 440 } 441