19b0f1823SDavid Xu /* 29b0f1823SDavid Xu * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 39b0f1823SDavid Xu * All rights reserved. 49b0f1823SDavid Xu * 59b0f1823SDavid Xu * Redistribution and use in source and binary forms, with or without 69b0f1823SDavid Xu * modification, are permitted provided that the following conditions 79b0f1823SDavid Xu * are met: 89b0f1823SDavid Xu * 1. Redistributions of source code must retain the above copyright 99b0f1823SDavid Xu * notice(s), this list of conditions and the following disclaimer as 109b0f1823SDavid Xu * the first lines of this file unmodified other than the possible 119b0f1823SDavid Xu * addition of one or more copyright notices. 129b0f1823SDavid Xu * 2. Redistributions in binary form must reproduce the above copyright 139b0f1823SDavid Xu * notice(s), this list of conditions and the following disclaimer in 149b0f1823SDavid Xu * the documentation and/or other materials provided with the 159b0f1823SDavid Xu * distribution. 169b0f1823SDavid Xu * 179b0f1823SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 189b0f1823SDavid Xu * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 199b0f1823SDavid Xu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 209b0f1823SDavid Xu * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 219b0f1823SDavid Xu * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 229b0f1823SDavid Xu * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 239b0f1823SDavid Xu * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 249b0f1823SDavid Xu * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 259b0f1823SDavid Xu * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 269b0f1823SDavid Xu * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 279b0f1823SDavid Xu * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 289b0f1823SDavid Xu * 299b0f1823SDavid Xu * $FreeBSD$ 309b0f1823SDavid Xu */ 319b0f1823SDavid Xu 329b0f1823SDavid Xu #include "namespace.h" 339b0f1823SDavid Xu #include <sys/types.h> 349b0f1823SDavid Xu #include <sys/queue.h> 359b0f1823SDavid Xu #include <sys/mman.h> 369b0f1823SDavid Xu #include <sys/stat.h> 379b0f1823SDavid Xu #include <errno.h> 389b0f1823SDavid Xu #include <machine/atomic.h> 399b0f1823SDavid Xu #include <sys/umtx.h> 409b0f1823SDavid Xu #include <limits.h> 419b0f1823SDavid Xu #include <fcntl.h> 429b0f1823SDavid Xu #include <pthread.h> 439b0f1823SDavid Xu #include <stdarg.h> 449b0f1823SDavid Xu #include <stdlib.h> 459b0f1823SDavid Xu #include <string.h> 469b0f1823SDavid Xu #include <time.h> 479b0f1823SDavid Xu #include <semaphore.h> 489b0f1823SDavid Xu #include <unistd.h> 499b0f1823SDavid Xu #include "un-namespace.h" 509b0f1823SDavid Xu 519b0f1823SDavid Xu __weak_reference(_libc_sem_close, sem_close); 529b0f1823SDavid Xu __weak_reference(_libc_sem_close, _sem_close); 539b0f1823SDavid Xu __weak_reference(_libc_sem_destroy, sem_destroy); 549b0f1823SDavid Xu __weak_reference(_libc_sem_destroy, _sem_destroy); 559b0f1823SDavid Xu __weak_reference(_libc_sem_getvalue, sem_getvalue); 569b0f1823SDavid Xu __weak_reference(_libc_sem_getvalue, _sem_getvalue); 579b0f1823SDavid Xu __weak_reference(_libc_sem_init, sem_init); 589b0f1823SDavid Xu __weak_reference(_libc_sem_init, _sem_init); 599b0f1823SDavid Xu __weak_reference(_libc_sem_open, sem_open); 609b0f1823SDavid Xu __weak_reference(_libc_sem_open, _sem_open); 619b0f1823SDavid Xu __weak_reference(_libc_sem_post, sem_post); 629b0f1823SDavid Xu __weak_reference(_libc_sem_post, _sem_post); 639b0f1823SDavid Xu __weak_reference(_libc_sem_timedwait, sem_timedwait); 649b0f1823SDavid Xu __weak_reference(_libc_sem_timedwait, _sem_timedwait); 659b0f1823SDavid Xu __weak_reference(_libc_sem_trywait, sem_trywait); 669b0f1823SDavid Xu __weak_reference(_libc_sem_trywait, _sem_trywait); 679b0f1823SDavid Xu __weak_reference(_libc_sem_unlink, sem_unlink); 689b0f1823SDavid Xu __weak_reference(_libc_sem_unlink, _sem_unlink); 699b0f1823SDavid Xu __weak_reference(_libc_sem_wait, sem_wait); 709b0f1823SDavid Xu __weak_reference(_libc_sem_wait, _sem_wait); 719b0f1823SDavid Xu 729b0f1823SDavid Xu #define SEM_PREFIX "/tmp/SEMD" 739b0f1823SDavid Xu #define SEM_MAGIC ((u_int32_t)0x73656d31) 749b0f1823SDavid Xu 759b0f1823SDavid Xu struct sem_nameinfo { 769b0f1823SDavid Xu int open_count; 779b0f1823SDavid Xu char *name; 789b0f1823SDavid Xu sem_t *sem; 799b0f1823SDavid Xu LIST_ENTRY(sem_nameinfo) next; 809b0f1823SDavid Xu }; 819b0f1823SDavid Xu 829b0f1823SDavid Xu static pthread_once_t once = PTHREAD_ONCE_INIT; 839b0f1823SDavid Xu static pthread_mutex_t sem_llock; 849b0f1823SDavid Xu static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 859b0f1823SDavid Xu 869b0f1823SDavid Xu static void 879b0f1823SDavid Xu sem_prefork() 889b0f1823SDavid Xu { 899b0f1823SDavid Xu 909b0f1823SDavid Xu _pthread_mutex_lock(&sem_llock); 919b0f1823SDavid Xu } 929b0f1823SDavid Xu 939b0f1823SDavid Xu static void 949b0f1823SDavid Xu sem_postfork() 959b0f1823SDavid Xu { 969b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 979b0f1823SDavid Xu } 989b0f1823SDavid Xu 999b0f1823SDavid Xu static void 1009b0f1823SDavid Xu sem_child_postfork() 1019b0f1823SDavid Xu { 1029b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 1039b0f1823SDavid Xu } 1049b0f1823SDavid Xu 1059b0f1823SDavid Xu static void 1069b0f1823SDavid Xu sem_module_init(void) 1079b0f1823SDavid Xu { 1089b0f1823SDavid Xu pthread_mutexattr_t ma; 1099b0f1823SDavid Xu 1109b0f1823SDavid Xu _pthread_mutexattr_init(&ma); 1119b0f1823SDavid Xu _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 1129b0f1823SDavid Xu _pthread_mutex_init(&sem_llock, &ma); 1139b0f1823SDavid Xu _pthread_mutexattr_destroy(&ma); 1149b0f1823SDavid Xu _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 1159b0f1823SDavid Xu } 1169b0f1823SDavid Xu 1179b0f1823SDavid Xu static inline int 1189b0f1823SDavid Xu sem_check_validity(sem_t *sem) 1199b0f1823SDavid Xu { 1209b0f1823SDavid Xu 1219b0f1823SDavid Xu if (sem->_magic == SEM_MAGIC) 1229b0f1823SDavid Xu return (0); 1239b0f1823SDavid Xu else { 1249b0f1823SDavid Xu errno = EINVAL; 1259b0f1823SDavid Xu return (-1); 1269b0f1823SDavid Xu } 1279b0f1823SDavid Xu } 1289b0f1823SDavid Xu 1299b0f1823SDavid Xu int 1309b0f1823SDavid Xu _libc_sem_init(sem_t *sem, int pshared, unsigned int value) 1319b0f1823SDavid Xu { 1329b0f1823SDavid Xu 1339b0f1823SDavid Xu if (value > SEM_VALUE_MAX) { 1349b0f1823SDavid Xu errno = EINVAL; 1359b0f1823SDavid Xu return (-1); 1369b0f1823SDavid Xu } 1379b0f1823SDavid Xu 1389b0f1823SDavid Xu bzero(sem, sizeof(sem_t)); 1399b0f1823SDavid Xu sem->_magic = SEM_MAGIC; 1409b0f1823SDavid Xu sem->_kern._count = (u_int32_t)value; 1419b0f1823SDavid Xu sem->_kern._has_waiters = 0; 1429b0f1823SDavid Xu sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 1439b0f1823SDavid Xu return (0); 1449b0f1823SDavid Xu } 1459b0f1823SDavid Xu 1469b0f1823SDavid Xu sem_t * 1479b0f1823SDavid Xu _libc_sem_open(const char *name, int flags, ...) 1489b0f1823SDavid Xu { 1499b0f1823SDavid Xu char path[PATH_MAX]; 1509b0f1823SDavid Xu 1519b0f1823SDavid Xu struct stat sb; 1529b0f1823SDavid Xu va_list ap; 1539b0f1823SDavid Xu struct sem_nameinfo *ni = NULL; 1549b0f1823SDavid Xu sem_t *sem = NULL; 1559b0f1823SDavid Xu int fd = -1, mode, len; 1569b0f1823SDavid Xu 1579b0f1823SDavid Xu if (name[0] != '/') { 1589b0f1823SDavid Xu errno = EINVAL; 1599b0f1823SDavid Xu return (NULL); 1609b0f1823SDavid Xu } 1619b0f1823SDavid Xu name++; 1629b0f1823SDavid Xu 1639b0f1823SDavid Xu if (flags & ~(O_CREAT|O_EXCL)) { 1649b0f1823SDavid Xu errno = EINVAL; 1659b0f1823SDavid Xu return (NULL); 1669b0f1823SDavid Xu } 1679b0f1823SDavid Xu 1689b0f1823SDavid Xu _pthread_once(&once, sem_module_init); 1699b0f1823SDavid Xu 1709b0f1823SDavid Xu _pthread_mutex_lock(&sem_llock); 1719b0f1823SDavid Xu LIST_FOREACH(ni, &sem_list, next) { 1729b0f1823SDavid Xu if (strcmp(name, ni->name) == 0) { 1739b0f1823SDavid Xu ni->open_count++; 1749b0f1823SDavid Xu sem = ni->sem; 1759b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 1769b0f1823SDavid Xu return (sem); 1779b0f1823SDavid Xu } 1789b0f1823SDavid Xu } 1799b0f1823SDavid Xu 1809b0f1823SDavid Xu if (flags & O_CREAT) { 1819b0f1823SDavid Xu va_start(ap, flags); 1829b0f1823SDavid Xu mode = va_arg(ap, int); 1839b0f1823SDavid Xu va_end(ap); 1849b0f1823SDavid Xu } 1859b0f1823SDavid Xu 1869b0f1823SDavid Xu len = sizeof(*ni) + strlen(name) + 1; 1879b0f1823SDavid Xu ni = (struct sem_nameinfo *)malloc(len); 1889b0f1823SDavid Xu if (ni == NULL) { 1899b0f1823SDavid Xu errno = ENOSPC; 1909b0f1823SDavid Xu goto error; 1919b0f1823SDavid Xu } 1929b0f1823SDavid Xu 1939b0f1823SDavid Xu ni->name = (char *)(ni+1); 1949b0f1823SDavid Xu strcpy(ni->name, name); 1959b0f1823SDavid Xu 1969b0f1823SDavid Xu strcpy(path, SEM_PREFIX); 1979b0f1823SDavid Xu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 1989b0f1823SDavid Xu errno = ENAMETOOLONG; 1999b0f1823SDavid Xu goto error; 2009b0f1823SDavid Xu } 2019b0f1823SDavid Xu 2029b0f1823SDavid Xu fd = _open(path, flags|O_RDWR, mode); 2039b0f1823SDavid Xu if (fd == -1) 2049b0f1823SDavid Xu goto error; 2059b0f1823SDavid Xu if (flock(fd, LOCK_EX) == -1) 2069b0f1823SDavid Xu goto error; 2079b0f1823SDavid Xu if (_fstat(fd, &sb)) { 2089b0f1823SDavid Xu flock(fd, LOCK_UN); 2099b0f1823SDavid Xu goto error; 2109b0f1823SDavid Xu } 2119b0f1823SDavid Xu if (sb.st_size < sizeof(sem_t)) { 2129b0f1823SDavid Xu sem_t tmp; 2139b0f1823SDavid Xu 2149b0f1823SDavid Xu tmp._magic = SEM_MAGIC; 2159b0f1823SDavid Xu tmp._kern._has_waiters = 0; 2169b0f1823SDavid Xu tmp._kern._count = 0; 2179b0f1823SDavid Xu tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 2189b0f1823SDavid Xu if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { 2199b0f1823SDavid Xu flock(fd, LOCK_UN); 2209b0f1823SDavid Xu goto error; 2219b0f1823SDavid Xu } 2229b0f1823SDavid Xu } 2239b0f1823SDavid Xu flock(fd, LOCK_UN); 2249b0f1823SDavid Xu sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 2259b0f1823SDavid Xu MAP_SHARED|MAP_NOSYNC, fd, 0); 2269b0f1823SDavid Xu if (sem == MAP_FAILED) { 2279b0f1823SDavid Xu sem = NULL; 2289b0f1823SDavid Xu if (errno == ENOMEM) 2299b0f1823SDavid Xu errno = ENOSPC; 2309b0f1823SDavid Xu goto error; 2319b0f1823SDavid Xu } 2329b0f1823SDavid Xu if (sem->_magic != SEM_MAGIC) { 2339b0f1823SDavid Xu errno = EINVAL; 2349b0f1823SDavid Xu goto error; 2359b0f1823SDavid Xu } 2369b0f1823SDavid Xu ni->open_count = 1; 2379b0f1823SDavid Xu ni->sem = sem; 2389b0f1823SDavid Xu LIST_INSERT_HEAD(&sem_list, ni, next); 2399b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 2409b0f1823SDavid Xu _close(fd); 2419b0f1823SDavid Xu return (sem); 2429b0f1823SDavid Xu 2439b0f1823SDavid Xu error: 2449b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 2459b0f1823SDavid Xu if (fd != -1) 2469b0f1823SDavid Xu _close(fd); 2479b0f1823SDavid Xu if (sem != NULL) 2489b0f1823SDavid Xu munmap(sem, sizeof(sem_t)); 2499b0f1823SDavid Xu free(ni); 2509b0f1823SDavid Xu return (SEM_FAILED); 2519b0f1823SDavid Xu } 2529b0f1823SDavid Xu 2539b0f1823SDavid Xu int 2549b0f1823SDavid Xu _libc_sem_close(sem_t *sem) 2559b0f1823SDavid Xu { 2569b0f1823SDavid Xu struct sem_nameinfo *ni; 2579b0f1823SDavid Xu 2589b0f1823SDavid Xu if (sem_check_validity(sem) != 0) 2599b0f1823SDavid Xu return (-1); 2609b0f1823SDavid Xu 2619b0f1823SDavid Xu if (!(sem->_kern._flags & SEM_NAMED)) { 2629b0f1823SDavid Xu errno = EINVAL; 2639b0f1823SDavid Xu return (-1); 2649b0f1823SDavid Xu } 2659b0f1823SDavid Xu 2669b0f1823SDavid Xu _pthread_mutex_lock(&sem_llock); 2679b0f1823SDavid Xu LIST_FOREACH(ni, &sem_list, next) { 2689b0f1823SDavid Xu if (sem == ni->sem) { 2699b0f1823SDavid Xu if (--ni->open_count > 0) { 2709b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 2719b0f1823SDavid Xu return (0); 2729b0f1823SDavid Xu } 2739b0f1823SDavid Xu else 2749b0f1823SDavid Xu break; 2759b0f1823SDavid Xu } 2769b0f1823SDavid Xu } 2779b0f1823SDavid Xu 2789b0f1823SDavid Xu if (ni) { 2799b0f1823SDavid Xu LIST_REMOVE(ni, next); 2809b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 2819b0f1823SDavid Xu munmap(sem, sizeof(*sem)); 2829b0f1823SDavid Xu free(ni); 2839b0f1823SDavid Xu return (0); 2849b0f1823SDavid Xu } 2859b0f1823SDavid Xu _pthread_mutex_unlock(&sem_llock); 2869b0f1823SDavid Xu return (-1); 2879b0f1823SDavid Xu } 2889b0f1823SDavid Xu 2899b0f1823SDavid Xu int 2909b0f1823SDavid Xu _libc_sem_unlink(const char *name) 2919b0f1823SDavid Xu { 2929b0f1823SDavid Xu char path[PATH_MAX]; 2939b0f1823SDavid Xu 2949b0f1823SDavid Xu if (name[0] != '/') { 2959b0f1823SDavid Xu errno = ENOENT; 2969b0f1823SDavid Xu return -1; 2979b0f1823SDavid Xu } 2989b0f1823SDavid Xu name++; 2999b0f1823SDavid Xu 3009b0f1823SDavid Xu strcpy(path, SEM_PREFIX); 3019b0f1823SDavid Xu if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 3029b0f1823SDavid Xu errno = ENAMETOOLONG; 3039b0f1823SDavid Xu return (-1); 3049b0f1823SDavid Xu } 3059b0f1823SDavid Xu return unlink(path); 3069b0f1823SDavid Xu } 3079b0f1823SDavid Xu 3089b0f1823SDavid Xu int 3099b0f1823SDavid Xu _libc_sem_destroy(sem_t *sem) 3109b0f1823SDavid Xu { 3119b0f1823SDavid Xu 3129b0f1823SDavid Xu if (sem_check_validity(sem) != 0) 3139b0f1823SDavid Xu return (-1); 3149b0f1823SDavid Xu 3159b0f1823SDavid Xu if (sem->_kern._flags & SEM_NAMED) { 3169b0f1823SDavid Xu errno = EINVAL; 3179b0f1823SDavid Xu return (-1); 3189b0f1823SDavid Xu } 3199b0f1823SDavid Xu sem->_magic = 0; 3209b0f1823SDavid Xu return (0); 3219b0f1823SDavid Xu } 3229b0f1823SDavid Xu 3239b0f1823SDavid Xu int 3249b0f1823SDavid Xu _libc_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 3259b0f1823SDavid Xu { 3269b0f1823SDavid Xu 3279b0f1823SDavid Xu if (sem_check_validity(sem) != 0) 3289b0f1823SDavid Xu return (-1); 3299b0f1823SDavid Xu 3309b0f1823SDavid Xu *sval = (int)sem->_kern._count; 3319b0f1823SDavid Xu return (0); 3329b0f1823SDavid Xu } 3339b0f1823SDavid Xu 334d802aa25SDavid Xu static __inline int 3359b0f1823SDavid Xu usem_wake(struct _usem *sem) 3369b0f1823SDavid Xu { 3379b0f1823SDavid Xu if (!sem->_has_waiters) 3389b0f1823SDavid Xu return (0); 3399b0f1823SDavid Xu return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); 3409b0f1823SDavid Xu } 3419b0f1823SDavid Xu 342d802aa25SDavid Xu static __inline int 3439b0f1823SDavid Xu usem_wait(struct _usem *sem, const struct timespec *timeout) 3449b0f1823SDavid Xu { 3459b0f1823SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 3469b0f1823SDavid Xu timeout->tv_nsec <= 0))) { 3479b0f1823SDavid Xu errno = ETIMEDOUT; 3489b0f1823SDavid Xu return (-1); 3499b0f1823SDavid Xu } 3509b0f1823SDavid Xu return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, 3519b0f1823SDavid Xu __DECONST(void*, timeout)); 3529b0f1823SDavid Xu } 3539b0f1823SDavid Xu 3549b0f1823SDavid Xu int 3559b0f1823SDavid Xu _libc_sem_trywait(sem_t *sem) 3569b0f1823SDavid Xu { 3579b0f1823SDavid Xu int val; 3589b0f1823SDavid Xu 3599b0f1823SDavid Xu if (sem_check_validity(sem) != 0) 3609b0f1823SDavid Xu return (-1); 3619b0f1823SDavid Xu 3629b0f1823SDavid Xu while ((val = sem->_kern._count) > 0) { 3639b0f1823SDavid Xu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 3649b0f1823SDavid Xu return (0); 3659b0f1823SDavid Xu } 3669b0f1823SDavid Xu errno = EAGAIN; 3679b0f1823SDavid Xu return (-1); 3689b0f1823SDavid Xu } 3699b0f1823SDavid Xu 3709b0f1823SDavid Xu static void 3719b0f1823SDavid Xu sem_cancel_handler(void *arg) 3729b0f1823SDavid Xu { 3739b0f1823SDavid Xu sem_t *sem = arg; 3749b0f1823SDavid Xu 3759b0f1823SDavid Xu if (sem->_kern._has_waiters && sem->_kern._count) 3769b0f1823SDavid Xu usem_wake(&sem->_kern); 3779b0f1823SDavid Xu } 3789b0f1823SDavid Xu 3799b0f1823SDavid Xu #define TIMESPEC_SUB(dst, src, val) \ 3809b0f1823SDavid Xu do { \ 3819b0f1823SDavid Xu (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 3829b0f1823SDavid Xu (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 3839b0f1823SDavid Xu if ((dst)->tv_nsec < 0) { \ 3849b0f1823SDavid Xu (dst)->tv_sec--; \ 3859b0f1823SDavid Xu (dst)->tv_nsec += 1000000000; \ 3869b0f1823SDavid Xu } \ 3879b0f1823SDavid Xu } while (0) 3889b0f1823SDavid Xu 3899b0f1823SDavid Xu 390d802aa25SDavid Xu static __inline int 3919b0f1823SDavid Xu enable_async_cancel(void) 3929b0f1823SDavid Xu { 3939b0f1823SDavid Xu int old; 3949b0f1823SDavid Xu 3959b0f1823SDavid Xu _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); 3969b0f1823SDavid Xu return (old); 3979b0f1823SDavid Xu } 3989b0f1823SDavid Xu 399d802aa25SDavid Xu static __inline void 4009b0f1823SDavid Xu restore_async_cancel(int val) 4019b0f1823SDavid Xu { 4029b0f1823SDavid Xu _pthread_setcanceltype(val, NULL); 4039b0f1823SDavid Xu } 4049b0f1823SDavid Xu 4059b0f1823SDavid Xu int 4069b0f1823SDavid Xu _libc_sem_timedwait(sem_t * __restrict sem, 4079b0f1823SDavid Xu const struct timespec * __restrict abstime) 4089b0f1823SDavid Xu { 4099b0f1823SDavid Xu struct timespec ts, ts2; 4109b0f1823SDavid Xu int val, retval, saved_cancel; 4119b0f1823SDavid Xu 4129b0f1823SDavid Xu if (sem_check_validity(sem) != 0) 4139b0f1823SDavid Xu return (-1); 4149b0f1823SDavid Xu 4159b0f1823SDavid Xu retval = 0; 4169b0f1823SDavid Xu for (;;) { 4179b0f1823SDavid Xu while ((val = sem->_kern._count) > 0) { 4189b0f1823SDavid Xu if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 4199b0f1823SDavid Xu return (0); 4209b0f1823SDavid Xu } 4219b0f1823SDavid Xu 4229b0f1823SDavid Xu if (retval) 4239b0f1823SDavid Xu break; 4249b0f1823SDavid Xu 4259b0f1823SDavid Xu /* 4269b0f1823SDavid Xu * The timeout argument is only supposed to 4279b0f1823SDavid Xu * be checked if the thread would have blocked. 4289b0f1823SDavid Xu */ 4299b0f1823SDavid Xu if (abstime != NULL) { 4309b0f1823SDavid Xu if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 4319b0f1823SDavid Xu errno = EINVAL; 4329b0f1823SDavid Xu return (-1); 4339b0f1823SDavid Xu } 4349b0f1823SDavid Xu clock_gettime(CLOCK_REALTIME, &ts); 4359b0f1823SDavid Xu TIMESPEC_SUB(&ts2, abstime, &ts); 4369b0f1823SDavid Xu } 4379b0f1823SDavid Xu pthread_cleanup_push(sem_cancel_handler, sem); 4389b0f1823SDavid Xu saved_cancel = enable_async_cancel(); 4399b0f1823SDavid Xu retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); 4409b0f1823SDavid Xu restore_async_cancel(saved_cancel); 4419b0f1823SDavid Xu pthread_cleanup_pop(0); 4429b0f1823SDavid Xu } 4439b0f1823SDavid Xu return (retval); 4449b0f1823SDavid Xu } 4459b0f1823SDavid Xu 4469b0f1823SDavid Xu int 4479b0f1823SDavid Xu _libc_sem_wait(sem_t *sem) 4489b0f1823SDavid Xu { 4499b0f1823SDavid Xu return _libc_sem_timedwait(sem, NULL); 4509b0f1823SDavid Xu } 4519b0f1823SDavid Xu 4529b0f1823SDavid Xu /* 4539b0f1823SDavid Xu * POSIX: 4549b0f1823SDavid Xu * The sem_post() interface is reentrant with respect to signals and may be 4559b0f1823SDavid Xu * invoked from a signal-catching function. 4569b0f1823SDavid Xu * The implementation does not use lock, so it should be safe. 4579b0f1823SDavid Xu */ 4589b0f1823SDavid Xu int 4599b0f1823SDavid Xu _libc_sem_post(sem_t *sem) 4609b0f1823SDavid Xu { 4619b0f1823SDavid Xu 4629b0f1823SDavid Xu if (sem_check_validity(sem) != 0) 4639b0f1823SDavid Xu return (-1); 4649b0f1823SDavid Xu 4659b0f1823SDavid Xu atomic_add_rel_int(&sem->_kern._count, 1); 4669b0f1823SDavid Xu return usem_wake(&sem->_kern); 4679b0f1823SDavid Xu } 468