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, 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 242 error: 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 254 int 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 293 int 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 312 int 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 327 int 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 338 static __inline int 339 usem_wake(struct _usem *sem) 340 { 341 return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 342 } 343 344 static __inline int 345 usem_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 364 int 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 380 int 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 418 int 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 */ 430 int 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