diff options
| author | Elliott Hughes <enh@google.com> | 2014-09-18 16:11:59 -0700 |
|---|---|---|
| committer | Elliott Hughes <enh@google.com> | 2014-09-19 17:37:06 -0700 |
| commit | 04303f5a8ab9a992f3671d46b6ee2171582cbd61 (patch) | |
| tree | 98d3997d33e93eeb91a2c2f331b57f35c180cbb2 | |
| parent | adc01348ee51a4ad678b1c277f85cbbed5c2e728 (diff) | |
| download | bionic-04303f5a8ab9a992f3671d46b6ee2171582cbd61.zip bionic-04303f5a8ab9a992f3671d46b6ee2171582cbd61.tar.gz bionic-04303f5a8ab9a992f3671d46b6ee2171582cbd61.tar.bz2 | |
Add semaphore tests, fix sem_destroy.
Bug: https://code.google.com/p/android/issues/detail?id=76088
Change-Id: I4a0561b23e90312384d40a1c804ca64ee98f4066
| -rw-r--r-- | libc/Android.mk | 2 | ||||
| -rw-r--r-- | libc/bionic/bionic_time_conversions.cpp | 18 | ||||
| -rw-r--r-- | libc/bionic/pthread_cond.cpp | 6 | ||||
| -rw-r--r-- | libc/bionic/pthread_internal.h | 2 | ||||
| -rw-r--r-- | libc/bionic/pthread_internals.cpp | 16 | ||||
| -rw-r--r-- | libc/bionic/pthread_mutex.cpp | 14 | ||||
| -rw-r--r-- | libc/bionic/pthread_rwlock.cpp | 3 | ||||
| -rw-r--r-- | libc/bionic/semaphore.c | 398 | ||||
| -rw-r--r-- | libc/bionic/semaphore.cpp | 322 | ||||
| -rw-r--r-- | libc/bionic/sysconf.cpp | 133 | ||||
| -rw-r--r-- | libc/include/limits.h | 4 | ||||
| -rw-r--r-- | libc/include/semaphore.h | 29 | ||||
| -rw-r--r-- | libc/private/bionic_constants.h | 22 | ||||
| -rw-r--r-- | libc/private/bionic_time_conversions.h | 2 | ||||
| -rw-r--r-- | tests/Android.mk | 1 | ||||
| -rw-r--r-- | tests/semaphore_test.cpp | 143 | ||||
| -rw-r--r-- | tests/time_test.cpp | 4 |
17 files changed, 568 insertions, 551 deletions
diff --git a/libc/Android.mk b/libc/Android.mk index 0f828ed..489200f 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -53,7 +53,6 @@ libc_common_src_files := \ bionic/pututline.c \ bionic/sched_cpualloc.c \ bionic/sched_cpucount.c \ - bionic/semaphore.c \ bionic/sigblock.c \ bionic/siginterrupt.c \ bionic/sigsetmask.c \ @@ -182,6 +181,7 @@ libc_bionic_src_files := \ bionic/scandir.cpp \ bionic/sched_getaffinity.cpp \ bionic/sched_getcpu.cpp \ + bionic/semaphore.cpp \ bionic/send.cpp \ bionic/setegid.cpp \ bionic/__set_errno.cpp \ diff --git a/libc/bionic/bionic_time_conversions.cpp b/libc/bionic/bionic_time_conversions.cpp index 7f3c026..75e8d49 100644 --- a/libc/bionic/bionic_time_conversions.cpp +++ b/libc/bionic/bionic_time_conversions.cpp @@ -28,6 +28,8 @@ #include "private/bionic_time_conversions.h" +#include "private/bionic_constants.h" + bool timespec_from_timeval(timespec& ts, const timeval& tv) { // Whole seconds can just be copied. ts.tv_sec = tv.tv_sec; @@ -49,3 +51,19 @@ void timeval_from_timespec(timeval& tv, const timespec& ts) { tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; } + +// Initializes 'ts' with the difference between 'abs_ts' and the current time +// according to 'clock'. Returns false if abstime already expired, true otherwise. +bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock) { + clock_gettime(clock, &ts); + ts.tv_sec = abs_ts.tv_sec - ts.tv_sec; + ts.tv_nsec = abs_ts.tv_nsec - ts.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_sec--; + ts.tv_nsec += NS_PER_S; + } + if (ts.tv_nsec < 0 || ts.tv_sec < 0) { + return false; + } + return true; +} diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp index e623b62..32ff81a 100644 --- a/libc/bionic/pthread_cond.cpp +++ b/libc/bionic/pthread_cond.cpp @@ -163,12 +163,12 @@ int __pthread_cond_timedwait_relative(pthread_cond_t* cond, pthread_mutex_t* mut } __LIBC_HIDDEN__ -int __pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime, clockid_t clock) { +int __pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abs_ts, clockid_t clock) { timespec ts; timespec* tsp; - if (abstime != NULL) { - if (__timespec_from_absolute(&ts, abstime, clock) < 0) { + if (abs_ts != NULL) { + if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) { return ETIMEDOUT; } tsp = &ts; diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index a5b3002..392e781 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -117,8 +117,6 @@ __LIBC_HIDDEN__ void _pthread_internal_remove_locked(pthread_internal_t* thread) __LIBC_HIDDEN__ extern pthread_internal_t* g_thread_list; __LIBC_HIDDEN__ extern pthread_mutex_t g_thread_list_lock; -__LIBC_HIDDEN__ int __timespec_from_absolute(timespec*, const timespec*, clockid_t); - /* Needed by fork. */ __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare(); __LIBC_HIDDEN__ extern void __bionic_atfork_run_child(); diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internals.cpp index 4c08ba8..19c00d4 100644 --- a/libc/bionic/pthread_internals.cpp +++ b/libc/bionic/pthread_internals.cpp @@ -67,19 +67,3 @@ void _pthread_internal_add(pthread_internal_t* thread) { pthread_internal_t* __get_thread(void) { return reinterpret_cast<pthread_internal_t*>(__get_tls()[TLS_SLOT_THREAD_ID]); } - -// Initialize 'ts' with the difference between 'abstime' and the current time -// according to 'clock'. Returns -1 if abstime already expired, or 0 otherwise. -int __timespec_from_absolute(timespec* ts, const timespec* abstime, clockid_t clock) { - clock_gettime(clock, ts); - ts->tv_sec = abstime->tv_sec - ts->tv_sec; - ts->tv_nsec = abstime->tv_nsec - ts->tv_nsec; - if (ts->tv_nsec < 0) { - ts->tv_sec--; - ts->tv_nsec += 1000000000; - } - if ((ts->tv_nsec < 0) || (ts->tv_sec < 0)) { - return -1; - } - return 0; -} diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index e00ffb4..cbb6ef7 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -36,7 +36,9 @@ #include "pthread_internal.h" #include "private/bionic_atomic_inline.h" +#include "private/bionic_constants.h" #include "private/bionic_futex.h" +#include "private/bionic_time_conversions.h" #include "private/bionic_tls.h" #include "private/bionic_systrace.h" @@ -615,7 +617,7 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex) { return EBUSY; } -static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_timeout, clockid_t clock) { +static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_ts, clockid_t clock) { timespec ts; int mvalue = mutex->value; @@ -638,7 +640,7 @@ static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs // Loop while needed. while (__bionic_swap(locked_contended, &mutex->value) != unlocked) { - if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) { + if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) { return ETIMEDOUT; } __futex_wait_ex(&mutex->value, shared, locked_contended, &ts); @@ -681,7 +683,7 @@ static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs } // The value changed before we could lock it. We need to check // the time to avoid livelocks, reload the value, then loop again. - if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) { + if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) { return ETIMEDOUT; } @@ -703,7 +705,7 @@ static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs } // Check time and update 'ts'. - if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) { + if (timespec_from_absolute_timespec(ts, *abs_ts, clock)) { return ETIMEDOUT; } @@ -726,9 +728,9 @@ extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms clock_gettime(CLOCK_MONOTONIC, &abs_timeout); abs_timeout.tv_sec += ms / 1000; abs_timeout.tv_nsec += (ms % 1000) * 1000000; - if (abs_timeout.tv_nsec >= 1000000000) { + if (abs_timeout.tv_nsec >= NS_PER_S) { abs_timeout.tv_sec++; - abs_timeout.tv_nsec -= 1000000000; + abs_timeout.tv_nsec -= NS_PER_S; } int error = __pthread_mutex_timedlock(mutex, &abs_timeout, CLOCK_MONOTONIC); diff --git a/libc/bionic/pthread_rwlock.cpp b/libc/bionic/pthread_rwlock.cpp index 063137b..0d63457 100644 --- a/libc/bionic/pthread_rwlock.cpp +++ b/libc/bionic/pthread_rwlock.cpp @@ -30,6 +30,7 @@ #include "pthread_internal.h" #include "private/bionic_futex.h" +#include "private/bionic_time_conversions.h" /* Technical note: * @@ -71,7 +72,7 @@ static inline bool rwlock_is_shared(const pthread_rwlock_t* rwlock) { static bool timespec_from_absolute(timespec* rel_timeout, const timespec* abs_timeout) { if (abs_timeout != NULL) { - if (__timespec_from_absolute(rel_timeout, abs_timeout, CLOCK_REALTIME) < 0) { + if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout, CLOCK_REALTIME)) { return false; } } diff --git a/libc/bionic/semaphore.c b/libc/bionic/semaphore.c deleted file mode 100644 index 1fa019e..0000000 --- a/libc/bionic/semaphore.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include <semaphore.h> -#include <errno.h> -#include <sys/time.h> -#include <time.h> -#include <limits.h> - -#include "private/bionic_atomic_inline.h" -#include "private/bionic_futex.h" - -/* In this implementation, a semaphore contains a - * 31-bit signed value and a 1-bit 'shared' flag - * (for process-sharing purpose). - * - * We use the value -1 to indicate contention on the - * semaphore, 0 or more to indicate uncontended state, - * any value lower than -2 is invalid at runtime. - * - * State diagram: - * - * post(1) ==> 2 - * post(0) ==> 1 - * post(-1) ==> 1, then wake all waiters - * - * wait(2) ==> 1 - * wait(1) ==> 0 - * wait(0) ==> -1 then wait for a wake up + loop - * wait(-1) ==> -1 then wait for a wake up + loop - * - */ - -/* Use the upper 31-bits for the counter, and the lower one - * for the shared flag. - */ -#define SEMCOUNT_SHARED_MASK 0x00000001 -#define SEMCOUNT_VALUE_MASK 0xfffffffe -#define SEMCOUNT_VALUE_SHIFT 1 - -/* Maximum unsigned value that can be stored in the semaphore. - * One bit is used for the shared flag, another one for the - * sign bit, leaving us with only 30 bits. - */ -#define SEM_MAX_VALUE 0x3fffffff - -/* convert a value into the corresponding sem->count bit pattern */ -#define SEMCOUNT_FROM_VALUE(val) (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK) - -/* convert a sem->count bit pattern into the corresponding signed value */ -#define SEMCOUNT_TO_VALUE(sval) ((int)(sval) >> SEMCOUNT_VALUE_SHIFT) - -/* the value +1 as a sem->count bit-pattern. */ -#define SEMCOUNT_ONE SEMCOUNT_FROM_VALUE(1) - -/* the value -1 as a sem->count bit-pattern. */ -#define SEMCOUNT_MINUS_ONE SEMCOUNT_FROM_VALUE(-1) - -#define SEMCOUNT_DECREMENT(sval) (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK) -#define SEMCOUNT_INCREMENT(sval) (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK) - -/* return the shared bitflag from a semaphore */ -#define SEM_GET_SHARED(sem) ((sem)->count & SEMCOUNT_SHARED_MASK) - - -int sem_init(sem_t *sem, int pshared, unsigned int value) -{ - if (sem == NULL) { - errno = EINVAL; - return -1; - } - - /* ensure that 'value' can be stored in the semaphore */ - if (value > SEM_MAX_VALUE) { - errno = EINVAL; - return -1; - } - - sem->count = SEMCOUNT_FROM_VALUE(value); - if (pshared != 0) - sem->count |= SEMCOUNT_SHARED_MASK; - - return 0; -} - - -int sem_destroy(sem_t *sem) -{ - int count; - - if (sem == NULL) { - errno = EINVAL; - return -1; - } - count = SEMCOUNT_TO_VALUE(sem->count); - if (count < 0) { - errno = EBUSY; - return -1; - } - sem->count = 0; - return 0; -} - - -sem_t *sem_open(const char *name __unused, int oflag __unused, ...) -{ - errno = ENOSYS; - return SEM_FAILED; -} - - -int sem_close(sem_t *sem) -{ - if (sem == NULL) { - errno = EINVAL; - return -1; - } - errno = ENOSYS; - return -1; -} - - -int sem_unlink(const char* name __unused) -{ - errno = ENOSYS; - return -1; -} - - -/* Decrement a semaphore's value atomically, - * and return the old one. As a special case, - * this returns immediately if the value is - * negative (i.e. -1) - */ -static int -__sem_dec(volatile unsigned int *pvalue) -{ - unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); - unsigned int old, new; - int ret; - - do { - old = (*pvalue & SEMCOUNT_VALUE_MASK); - ret = SEMCOUNT_TO_VALUE(old); - if (ret < 0) - break; - - new = SEMCOUNT_DECREMENT(old); - } - while (__bionic_cmpxchg((int)(old|shared), - (int)(new|shared), - (volatile int *)pvalue) != 0); - return ret; -} - -/* Same as __sem_dec, but will not touch anything if the - * value is already negative *or* 0. Returns the old value. - */ -static int -__sem_trydec(volatile unsigned int *pvalue) -{ - unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); - unsigned int old, new; - int ret; - - do { - old = (*pvalue & SEMCOUNT_VALUE_MASK); - ret = SEMCOUNT_TO_VALUE(old); - if (ret <= 0) - break; - - new = SEMCOUNT_DECREMENT(old); - } - while (__bionic_cmpxchg((int)(old|shared), - (int)(new|shared), - (volatile int *)pvalue) != 0); - - return ret; -} - - -/* "Increment" the value of a semaphore atomically and - * return its old value. Note that this implements - * the special case of "incrementing" any negative - * value to +1 directly. - * - * NOTE: The value will _not_ wrap above SEM_VALUE_MAX - */ -static int -__sem_inc(volatile unsigned int *pvalue) -{ - unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK); - unsigned int old, new; - int ret; - - do { - old = (*pvalue & SEMCOUNT_VALUE_MASK); - ret = SEMCOUNT_TO_VALUE(old); - - /* Can't go higher than SEM_MAX_VALUE */ - if (ret == SEM_MAX_VALUE) - break; - - /* If the counter is negative, go directly to +1, - * otherwise just increment */ - if (ret < 0) - new = SEMCOUNT_ONE; - else - new = SEMCOUNT_INCREMENT(old); - } - while ( __bionic_cmpxchg((int)(old|shared), - (int)(new|shared), - (volatile int*)pvalue) != 0); - - return ret; -} - -/* lock a semaphore */ -int sem_wait(sem_t *sem) -{ - unsigned shared; - - if (sem == NULL) { - errno = EINVAL; - return -1; - } - - shared = SEM_GET_SHARED(sem); - - for (;;) { - if (__sem_dec(&sem->count) > 0) - break; - - __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL); - } - ANDROID_MEMBAR_FULL(); - return 0; -} - -int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) -{ - unsigned int shared; - - if (sem == NULL) { - errno = EINVAL; - return -1; - } - - /* POSIX says we need to try to decrement the semaphore - * before checking the timeout value. Note that if the - * value is currently 0, __sem_trydec() does nothing. - */ - if (__sem_trydec(&sem->count) > 0) { - ANDROID_MEMBAR_FULL(); - return 0; - } - - /* Check it as per Posix */ - if (abs_timeout == NULL || - abs_timeout->tv_sec < 0 || - abs_timeout->tv_nsec < 0 || - abs_timeout->tv_nsec >= 1000000000) - { - errno = EINVAL; - return -1; - } - - shared = SEM_GET_SHARED(sem); - - for (;;) { - struct timespec ts; - int ret; - - /* Posix mandates CLOCK_REALTIME here */ - clock_gettime( CLOCK_REALTIME, &ts ); - ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec; - ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec; - if (ts.tv_nsec < 0) { - ts.tv_nsec += 1000000000; - ts.tv_sec -= 1; - } - - if (ts.tv_sec < 0 || ts.tv_nsec < 0) { - errno = ETIMEDOUT; - return -1; - } - - /* Try to grab the semaphore. If the value was 0, this - * will also change it to -1 */ - if (__sem_dec(&sem->count) > 0) { - ANDROID_MEMBAR_FULL(); - break; - } - - /* Contention detected. wait for a wakeup event */ - ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts); - - /* return in case of timeout or interrupt */ - if (ret == -ETIMEDOUT || ret == -EINTR) { - errno = -ret; - return -1; - } - } - return 0; -} - -/* Unlock a semaphore */ -int sem_post(sem_t *sem) -{ - unsigned int shared; - int old; - - if (sem == NULL) - return EINVAL; - - shared = SEM_GET_SHARED(sem); - - ANDROID_MEMBAR_FULL(); - old = __sem_inc(&sem->count); - if (old < 0) { - /* contention on the semaphore, wake up all waiters */ - __futex_wake_ex(&sem->count, shared, INT_MAX); - } - else if (old == SEM_MAX_VALUE) { - /* overflow detected */ - errno = EOVERFLOW; - return -1; - } - - return 0; -} - -int sem_trywait(sem_t *sem) -{ - if (sem == NULL) { - errno = EINVAL; - return -1; - } - - if (__sem_trydec(&sem->count) > 0) { - ANDROID_MEMBAR_FULL(); - return 0; - } else { - errno = EAGAIN; - return -1; - } -} - -/* Note that Posix requires that sem_getvalue() returns, in - * case of contention, the negative of the number of waiting - * threads. - * - * However, code that depends on this negative value to be - * meaningful is most probably racy. The GLibc sem_getvalue() - * only returns the semaphore value, which is 0, in case of - * contention, so we will mimick this behaviour here instead - * for better compatibility. - */ -int sem_getvalue(sem_t *sem, int *sval) -{ - int val; - - if (sem == NULL || sval == NULL) { - errno = EINVAL; - return -1; - } - - val = SEMCOUNT_TO_VALUE(sem->count); - if (val < 0) - val = 0; - - *sval = val; - return 0; -} diff --git a/libc/bionic/semaphore.cpp b/libc/bionic/semaphore.cpp new file mode 100644 index 0000000..c23eb75 --- /dev/null +++ b/libc/bionic/semaphore.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <semaphore.h> +#include <errno.h> +#include <limits.h> +#include <sys/time.h> +#include <time.h> + +#include "private/bionic_atomic_inline.h" +#include "private/bionic_constants.h" +#include "private/bionic_futex.h" +#include "private/bionic_time_conversions.h" + +// In this implementation, a semaphore contains a +// 31-bit signed value and a 1-bit 'shared' flag +// (for process-sharing purpose). +// +// We use the value -1 to indicate contention on the +// semaphore, 0 or more to indicate uncontended state, +// any value lower than -2 is invalid at runtime. +// +// State diagram: +// +// post(1) ==> 2 +// post(0) ==> 1 +// post(-1) ==> 1, then wake all waiters +// +// wait(2) ==> 1 +// wait(1) ==> 0 +// wait(0) ==> -1 then wait for a wake up + loop +// wait(-1) ==> -1 then wait for a wake up + loop + +// Use the upper 31-bits for the counter, and the lower one +// for the shared flag. +#define SEMCOUNT_SHARED_MASK 0x00000001 +#define SEMCOUNT_VALUE_MASK 0xfffffffe +#define SEMCOUNT_VALUE_SHIFT 1 + +// Convert a value into the corresponding sem->count bit pattern. +#define SEMCOUNT_FROM_VALUE(val) (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK) + +// Convert a sem->count bit pattern into the corresponding signed value. +static inline int SEMCOUNT_TO_VALUE(uint32_t sval) { + return (static_cast<int>(sval) >> SEMCOUNT_VALUE_SHIFT); +} + +// The value +1 as a sem->count bit-pattern. +#define SEMCOUNT_ONE SEMCOUNT_FROM_VALUE(1) + +// The value -1 as a sem->count bit-pattern. +#define SEMCOUNT_MINUS_ONE SEMCOUNT_FROM_VALUE(-1) + +#define SEMCOUNT_DECREMENT(sval) (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK) +#define SEMCOUNT_INCREMENT(sval) (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK) + +// Return the shared bitflag from a semaphore. +static inline uint32_t SEM_GET_SHARED(sem_t* sem) { + return (sem->count & SEMCOUNT_SHARED_MASK); +} + + +int sem_init(sem_t* sem, int pshared, unsigned int value) { + if (sem == NULL) { + errno = EINVAL; + return -1; + } + + // Ensure that 'value' can be stored in the semaphore. + if (value > SEM_VALUE_MAX) { + errno = EINVAL; + return -1; + } + + sem->count = SEMCOUNT_FROM_VALUE(value); + if (pshared != 0) { + sem->count |= SEMCOUNT_SHARED_MASK; + } + return 0; +} + +int sem_destroy(sem_t* sem) { + if (sem == NULL) { + errno = EINVAL; + return -1; + } + return 0; +} + +sem_t* sem_open(const char*, int, ...) { + errno = ENOSYS; + return SEM_FAILED; +} + +int sem_close(sem_t*) { + errno = ENOSYS; + return -1; +} + +int sem_unlink(const char*) { + errno = ENOSYS; + return -1; +} + +// Decrement a semaphore's value atomically, +// and return the old one. As a special case, +// this returns immediately if the value is +// negative (i.e. -1) +static int __sem_dec(volatile uint32_t* sem) { + volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem); + uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK); + uint32_t old_value, new_value; + int ret; + + do { + old_value = (*sem & SEMCOUNT_VALUE_MASK); + ret = SEMCOUNT_TO_VALUE(old_value); + if (ret < 0) { + break; + } + + new_value = SEMCOUNT_DECREMENT(old_value); + } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0); + + return ret; +} + +// Same as __sem_dec, but will not touch anything if the +// value is already negative *or* 0. Returns the old value. +static int __sem_trydec(volatile uint32_t* sem) { + volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem); + uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK); + uint32_t old_value, new_value; + int ret; + + do { + old_value = (*sem & SEMCOUNT_VALUE_MASK); + ret = SEMCOUNT_TO_VALUE(old_value); + if (ret <= 0) { + break; + } + + new_value = SEMCOUNT_DECREMENT(old_value); + } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0); + + return ret; +} + + +// "Increment" the value of a semaphore atomically and +// return its old value. Note that this implements +// the special case of "incrementing" any negative +// value to +1 directly. +// +// NOTE: The value will _not_ wrap above SEM_VALUE_MAX +static int __sem_inc(volatile uint32_t* sem) { + volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem); + uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK); + uint32_t old_value, new_value; + int ret; + + do { + old_value = (*sem & SEMCOUNT_VALUE_MASK); + ret = SEMCOUNT_TO_VALUE(old_value); + + // Can't go higher than SEM_VALUE_MAX. + if (ret == SEM_VALUE_MAX) { + break; + } + + // If the counter is negative, go directly to +1, otherwise just increment. + if (ret < 0) { + new_value = SEMCOUNT_ONE; + } else { + new_value = SEMCOUNT_INCREMENT(old_value); + } + } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0); + + return ret; +} + +int sem_wait(sem_t* sem) { + if (sem == NULL) { + errno = EINVAL; + return -1; + } + + uint32_t shared = SEM_GET_SHARED(sem); + + while (true) { + if (__sem_dec(&sem->count) > 0) { + ANDROID_MEMBAR_FULL(); + return 0; + } + + __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL); + } +} + +int sem_timedwait(sem_t* sem, const timespec* abs_timeout) { + if (sem == NULL) { + errno = EINVAL; + return -1; + } + + // POSIX says we need to try to decrement the semaphore + // before checking the timeout value. Note that if the + // value is currently 0, __sem_trydec() does nothing. + if (__sem_trydec(&sem->count) > 0) { + ANDROID_MEMBAR_FULL(); + return 0; + } + + // Check it as per POSIX. + if (abs_timeout == NULL || abs_timeout->tv_sec < 0 || abs_timeout->tv_nsec < 0 || abs_timeout->tv_nsec >= NS_PER_S) { + errno = EINVAL; + return -1; + } + + uint32_t shared = SEM_GET_SHARED(sem); + + while (true) { + // POSIX mandates CLOCK_REALTIME here. + timespec ts; + if (!timespec_from_absolute_timespec(ts, *abs_timeout, CLOCK_REALTIME)) { + errno = ETIMEDOUT; + return -1; + } + + // Try to grab the semaphore. If the value was 0, this will also change it to -1. + if (__sem_dec(&sem->count) > 0) { + ANDROID_MEMBAR_FULL(); + break; + } + + // Contention detected. Wait for a wakeup event. + int ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts); + + // Return in case of timeout or interrupt. + if (ret == -ETIMEDOUT || ret == -EINTR) { + errno = -ret; + return -1; + } + } + return 0; +} + +int sem_post(sem_t* sem) { + if (sem == NULL) { + return EINVAL; + } + + uint32_t shared = SEM_GET_SHARED(sem); + + ANDROID_MEMBAR_FULL(); + int old_value = __sem_inc(&sem->count); + if (old_value < 0) { + // Contention on the semaphore. Wake up all waiters. + __futex_wake_ex(&sem->count, shared, INT_MAX); + } else if (old_value == SEM_VALUE_MAX) { + // Overflow detected. + errno = EOVERFLOW; + return -1; + } + + return 0; +} + +int sem_trywait(sem_t* sem) { + if (sem == NULL) { + errno = EINVAL; + return -1; + } + + if (__sem_trydec(&sem->count) > 0) { + ANDROID_MEMBAR_FULL(); + return 0; + } else { + errno = EAGAIN; + return -1; + } +} + +int sem_getvalue(sem_t* sem, int* sval) { + if (sem == NULL || sval == NULL) { + errno = EINVAL; + return -1; + } + + int val = SEMCOUNT_TO_VALUE(sem->count); + if (val < 0) { + val = 0; + } + + *sval = val; + return 0; +} diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp index d8aac4f..24a1ae7 100644 --- a/libc/bionic/sysconf.cpp +++ b/libc/bionic/sysconf.cpp @@ -49,7 +49,6 @@ #define SYSTEM_MQ_OPEN_MAX 8 #define SYSTEM_MQ_PRIO_MAX 32768 #define SYSTEM_SEM_NSEMS_MAX 256 -#define SYSTEM_SEM_VALUE_MAX 0x3fffffff /* see bionic/semaphore.c */ #define SYSTEM_SIGQUEUE_MAX 32 #define SYSTEM_TIMER_MAX 32 #define SYSTEM_LOGIN_NAME_MAX 256 @@ -151,33 +150,17 @@ static int __sysconf_monotonic_clock() { } int sysconf(int name) { - switch (name) { -#ifdef _POSIX_ARG_MAX + switch (name) { case _SC_ARG_MAX: return _POSIX_ARG_MAX; -#endif -#ifdef _POSIX2_BC_BASE_MAX case _SC_BC_BASE_MAX: return _POSIX2_BC_BASE_MAX; -#endif -#ifdef _POSIX2_BC_DIM_MAX case _SC_BC_DIM_MAX: return _POSIX2_BC_DIM_MAX; -#endif -#ifdef _POSIX2_BC_SCALE_MAX case _SC_BC_SCALE_MAX: return _POSIX2_BC_SCALE_MAX; -#endif -#ifdef _POSIX2_BC_STRING_MAX case _SC_BC_STRING_MAX: return _POSIX2_BC_STRING_MAX; -#endif case _SC_CHILD_MAX: return CHILD_MAX; case _SC_CLK_TCK: return SYSTEM_CLK_TCK; -#ifdef _POSIX2_COLL_WEIGHTS_MASK - case _SC_COLL_WEIGHTS_MAX: return _POSIX2_COLL_WEIGHTS_MASK; -#endif -#ifdef _POSIX2_EXPR_NEST_MAX - case _SC_EXPR_NEST_MAX: return _POSIX2_EXPR_NEST_MAX; -#endif -#ifdef _POSIX2_LINE_MAX + case _SC_COLL_WEIGHTS_MAX: return _POSIX2_COLL_WEIGHTS_MAX; + case _SC_EXPR_NEST_MAX: return _POSIX2_EXPR_NEST_MAX; case _SC_LINE_MAX: return _POSIX2_LINE_MAX; -#endif case _SC_NGROUPS_MAX: return NGROUPS_MAX; case _SC_OPEN_MAX: return OPEN_MAX; //case _SC_PASS_MAX: return PASS_MAX; @@ -191,42 +174,20 @@ int sysconf(int name) { case _SC_2_SW_DEV: return SYSTEM_2_SW_DEV; case _SC_2_UPE: return SYSTEM_2_UPE; case _SC_2_VERSION: return SYSTEM_2_VERSION; -#ifdef _POSIX_JOB_CONTROL case _SC_JOB_CONTROL: return _POSIX_JOB_CONTROL; -#endif -#ifdef _POSIX_SAVED_IDS case _SC_SAVED_IDS: return _POSIX_SAVED_IDS; -#endif -#ifdef _POSIX_VERSION case _SC_VERSION: return _POSIX_VERSION; -#endif - //case _SC_RE_DUP_<AX: return ; + case _SC_RE_DUP_MAX: return _POSIX_RE_DUP_MAX; case _SC_STREAM_MAX: return FOPEN_MAX; - //case _SC_TZNAME_MAX: return ; -#if _XOPEN_CRYPT + case _SC_TZNAME_MAX: return _POSIX_TZNAME_MAX; case _SC_XOPEN_CRYPT: return _XOPEN_CRYPT; -#endif -#ifdef _XOPEN_ENH_I18N case _SC_XOPEN_ENH_I18N: return _XOPEN_ENH_I18N; -#endif -#ifdef _XOPEN_SHM - case _SC_XOPEN_SHM: return _XOPEN_SHM; -#endif -#ifdef _XOPEN_VERSION + //case _SC_XOPEN_SHM: return _XOPEN_SHM; case _SC_XOPEN_VERSION: return _XOPEN_VERSION; -#endif -#ifdef _XOPEN_XCU_VERSION case _SC_XOPEN_XCU_VERSION: return _XOPEN_XCU_VERSION; -#endif -#ifdef _XOPEN_REALTIME case _SC_XOPEN_REALTIME: return _XOPEN_REALTIME; -#endif -#ifdef _XOPEN_REALTIME_THREADS case _SC_XOPEN_REALTIME_THREADS: return _XOPEN_REALTIME_THREADS; -#endif -#ifdef _XOPEN_LEGACY case _SC_XOPEN_LEGACY: return _XOPEN_LEGACY; -#endif case _SC_ATEXIT_MAX: return SYSTEM_ATEXIT_MAX; case _SC_IOV_MAX: return SYSTEM_IOV_MAX; @@ -234,71 +195,35 @@ int sysconf(int name) { case _SC_PAGE_SIZE: return PAGE_SIZE; -#ifdef _XOPEN_UNIX case _SC_XOPEN_UNIX: return _XOPEN_UNIX; -#endif // XXX: TODO: XBS5 nonsense -#ifdef AIO_LISTIO_MAX - case _SC_AIO_LISTIO_MAX: return AIO_LISTIO_MAX; -#endif -#ifdef AIO_MAX - case _SC_AIO_MAX: return AIO_MAX; -#endif -#ifdef AIO_PRIO_DELTA_MAX - case _SC_AIO_PRIO_DELTA_MAX: return AIO_PRIO_DELTA_MAX; -#endif + //case _SC_AIO_LISTIO_MAX: return AIO_LISTIO_MAX; + //case _SC_AIO_MAX: return AIO_MAX; + //case _SC_AIO_PRIO_DELTA_MAX: return AIO_PRIO_DELTA_MAX; case _SC_DELAYTIMER_MAX: return SYSTEM_DELAYTIMER_MAX; case _SC_MQ_OPEN_MAX: return SYSTEM_MQ_OPEN_MAX; case _SC_MQ_PRIO_MAX: return SYSTEM_MQ_PRIO_MAX; case _SC_RTSIG_MAX: return RTSIG_MAX; case _SC_SEM_NSEMS_MAX: return SYSTEM_SEM_NSEMS_MAX; - case _SC_SEM_VALUE_MAX: return SYSTEM_SEM_VALUE_MAX; + case _SC_SEM_VALUE_MAX: return SEM_VALUE_MAX; case _SC_SIGQUEUE_MAX: return SYSTEM_SIGQUEUE_MAX; case _SC_TIMER_MAX: return SYSTEM_TIMER_MAX; -#ifdef _POSIX_ASYNCHRONOUS_IO - case _SC_ASYNCHRONOUS_IO: return _POSIX_ASYNCHRONOUS_IO; -#endif -#ifdef _POSIX_FSYNC + //case _SC_ASYNCHRONOUS_IO: return _POSIX_ASYNCHRONOUS_IO; case _SC_FSYNC: return _POSIX_FSYNC; -#endif -#ifdef _POSIX_MAPPED_FILES case _SC_MAPPED_FILES: return _POSIX_MAPPED_FILES; -#endif -#ifdef _POSIX_MEMLOCK - case _SC_MEMLOCK: return _POSIX_MEMLOCK; -#endif -#ifdef _POSIX_MEMLOCK_RANGE - case _SC_MEMLOCK_RANGE: return _POSIX_MEMLOCK_RANGE -#endif -#ifdef _POSIX_MEMORY_PROTECTION - case _SC_MEMORY_PROTECTION: return _POSIX_MEMORY_PROTECTION; -#endif -#ifdef _POSIX_MESSAGE_PASSING - case _SC_MESSAGE_PASSING: return _POSIX_MESSAGE_PASSING; -#endif -#ifdef _POSIX_PRIORITIZED_IO - case _SC_PRIORITIZED_IO: return _POSIX_PRIORITIZED_IO; -#endif -#ifdef _POSIX_PRIORITY_SCHEDULING + //case _SC_MEMLOCK: return _POSIX_MEMLOCK; + //case _SC_MEMLOCK_RANGE: return _POSIX_MEMLOCK_RANGE; + //case _SC_MEMORY_PROTECTION: return _POSIX_MEMORY_PROTECTION; + //case _SC_MESSAGE_PASSING: return _POSIX_MESSAGE_PASSING; + //case _SC_PRIORITIZED_IO: return _POSIX_PRIORITIZED_IO; case _SC_PRIORITY_SCHEDULING: return _POSIX_PRIORITY_SCHEDULING; -#endif -#ifdef _POSIX_REALTIME_SIGNALS case _SC_REALTIME_SIGNALS: return _POSIX_REALTIME_SIGNALS; -#endif -#ifdef _POSIX_SEMAPHORES case _SC_SEMAPHORES: return _POSIX_SEMAPHORES; -#endif -#ifdef _POSIX_SHARED_MEMORY_OBJECTS - case _SC_SHARED_MEMORY_OBJECTS: return _POSIX_SHARED_MEMORY_OBJECTS; -#endif -#ifdef _POSIX_SYNCHRONIZED_IO + //case _SC_SHARED_MEMORY_OBJECTS: return _POSIX_SHARED_MEMORY_OBJECTS; case _SC_SYNCHRONIZED_IO: return _POSIX_SYNCHRONIZED_IO; -#endif -#ifdef _POSIX_TIMERS case _SC_TIMERS: return _POSIX_TIMERS; -#endif case _SC_GETGR_R_SIZE_MAX: return 1024; case _SC_GETPW_R_SIZE_MAX: return 1024; @@ -314,25 +239,15 @@ int sysconf(int name) { case _SC_THREAD_STACK_MIN: return PTHREAD_STACK_MIN; case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX; case _SC_TTY_NAME_MAX: return SYSTEM_TTY_NAME_MAX; -#ifdef _POSIX_THREADS case _SC_THREADS: return _POSIX_THREADS; -#endif case _SC_THREAD_ATTR_STACKADDR: return -1; // Removed in POSIX 2008 case _SC_THREAD_ATTR_STACKSIZE: return -1; // Removed in POSIX 2008 -#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING - case _SC_THREAD_PRIORITY_SCHEDULING: return _POSIX_THREAD_PRIORITY_SCHEDULING; -#endif -#ifdef _POSIX_THREAD_PRIO_INHERIT + //case _SC_THREAD_PRIORITY_SCHEDULING: return _POSIX_THREAD_PRIORITY_SCHEDULING; case _SC_THREAD_PRIO_INHERIT: return _POSIX_THREAD_PRIO_INHERIT; -#endif -#ifdef _POSIX_THREAD_PRIO_PROTECT case _SC_THREAD_PRIO_PROTECT: return _POSIX_THREAD_PRIO_PROTECT; -#endif -#ifdef _POSIX_THREAD_SAFE_FUNCTIONS - case _SC_THREAD_SAFE_FUNCTIONS: return _POSIX_THREAD_SAFE_FUNCTIONS -#endif + //case _SC_THREAD_SAFE_FUNCTIONS: return _POSIX_THREAD_SAFE_FUNCTIONS case _SC_MONOTONIC_CLOCK: return __sysconf_monotonic_clock(); case _SC_NPROCESSORS_CONF: return __sysconf_nprocessors_conf(); @@ -341,9 +256,9 @@ int sysconf(int name) { case _SC_AVPHYS_PAGES: return __sysconf_avphys_pages(); default: - /* Posix says EINVAL is the only error that shall be returned, - * but GLibc uses ENOSYS */ - errno = ENOSYS; - return -1; - } + // Posix says EINVAL is the only error that shall be returned, + // but glibc uses ENOSYS. + errno = ENOSYS; + return -1; + } } diff --git a/libc/include/limits.h b/libc/include/limits.h index fb09657..f9f0c79 100644 --- a/libc/include/limits.h +++ b/libc/include/limits.h @@ -49,6 +49,7 @@ #define _POSIX_PATH_MAX 256 #define _POSIX_PIPE_BUF 512 #define _POSIX_RE_DUP_MAX 255 +#define _POSIX_SEM_VALUE_MAX 32767 #define _POSIX_SSIZE_MAX 32767 #define _POSIX_STREAM_MAX 8 #define _POSIX_SYMLINK_MAX 255 @@ -125,4 +126,7 @@ /* glibc's PAGE_MASK is the bitwise negation of BSD's! TODO: remove? */ #define PAGE_MASK (~(PAGE_SIZE - 1)) +#define _POSIX_SEMAPHORES 200809L +#define SEM_VALUE_MAX 0x3fffffff + #endif /* !_LIMITS_H_ */ diff --git a/libc/include/semaphore.h b/libc/include/semaphore.h index 7ae3c3a..5827870 100644 --- a/libc/include/semaphore.h +++ b/libc/include/semaphore.h @@ -25,6 +25,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #ifndef _SEMAPHORE_H #define _SEMAPHORE_H @@ -32,6 +33,8 @@ __BEGIN_DECLS +struct timespec; + typedef struct { volatile unsigned int count; #ifdef __LP64__ @@ -41,20 +44,18 @@ typedef struct { #define SEM_FAILED NULL -extern int sem_init(sem_t *sem, int pshared, unsigned int value); - -extern int sem_close(sem_t *); -extern int sem_destroy(sem_t *); -extern int sem_getvalue(sem_t *, int *); -extern int sem_init(sem_t *, int, unsigned int); -extern sem_t *sem_open(const char *, int, ...); -extern int sem_post(sem_t *); -extern int sem_trywait(sem_t *); -extern int sem_unlink(const char *); -extern int sem_wait(sem_t *); - -struct timespec; -extern int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); +int sem_destroy(sem_t*); +int sem_getvalue(sem_t*, int*); +int sem_init(sem_t*, int, unsigned int); +int sem_post(sem_t*); +int sem_timedwait(sem_t*, const struct timespec*); +int sem_trywait(sem_t*); +int sem_wait(sem_t*); + +/* These aren't actually implemented. */ +sem_t* sem_open(const char*, int, ...); +int sem_close(sem_t*); +int sem_unlink(const char*); __END_DECLS diff --git a/libc/private/bionic_constants.h b/libc/private/bionic_constants.h new file mode 100644 index 0000000..9ae1c8d --- /dev/null +++ b/libc/private/bionic_constants.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BIONIC_CONSTANTS_H_ +#define _BIONIC_CONSTANTS_H_ + +#define NS_PER_S 1000000000 + +#endif // _BIONIC_CONSTANTS_H_ diff --git a/libc/private/bionic_time_conversions.h b/libc/private/bionic_time_conversions.h index 51f543f..cf0046a 100644 --- a/libc/private/bionic_time_conversions.h +++ b/libc/private/bionic_time_conversions.h @@ -39,6 +39,8 @@ __LIBC_HIDDEN__ void timespec_from_ms(timespec& ts, const int ms); __LIBC_HIDDEN__ void timeval_from_timespec(timeval& tv, const timespec& ts); +__LIBC_HIDDEN__ bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock); + __END_DECLS #endif diff --git a/tests/Android.mk b/tests/Android.mk index 890e203..59fe57d 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -86,6 +86,7 @@ libBionicStandardTests_src_files := \ regex_test.cpp \ sched_test.cpp \ search_test.cpp \ + semaphore_test.cpp \ signal_test.cpp \ stack_protector_test.cpp \ stdatomic_test.cpp \ diff --git a/tests/semaphore_test.cpp b/tests/semaphore_test.cpp new file mode 100644 index 0000000..e517f81 --- /dev/null +++ b/tests/semaphore_test.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <semaphore.h> + +#include <errno.h> +#include <gtest/gtest.h> +#include <limits.h> +#include <pthread.h> +#include <time.h> +#include <unistd.h> + +#include "private/bionic_constants.h" + +TEST(semaphore, sem_init) { + sem_t s; + + // Perfectly fine initial values. + ASSERT_EQ(0, sem_init(&s, 0, 0)); + ASSERT_EQ(0, sem_init(&s, 0, 1)); + ASSERT_EQ(0, sem_init(&s, 0, 123)); + + // Too small an initial value. + errno = 0; + ASSERT_EQ(-1, sem_init(&s, 0, -1)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(SEM_VALUE_MAX, sysconf(_SC_SEM_VALUE_MAX)); + + // The largest initial value. + ASSERT_EQ(0, sem_init(&s, 0, SEM_VALUE_MAX)); + + // Too large an initial value. + errno = 0; + ASSERT_EQ(-1, sem_init(&s, 0, SEM_VALUE_MAX + 1)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(0, sem_destroy(&s)); +} + +TEST(semaphore, sem_trywait) { + sem_t s; + ASSERT_EQ(0, sem_init(&s, 0, 3)); + ASSERT_EQ(0, sem_trywait(&s)); + ASSERT_EQ(0, sem_trywait(&s)); + ASSERT_EQ(0, sem_trywait(&s)); + errno = 0; + ASSERT_EQ(-1, sem_trywait(&s)); + ASSERT_EQ(EAGAIN, errno); + ASSERT_EQ(0, sem_destroy(&s)); +} + +static void SemWaitThreadTestFn(sem_t& sem) { + ASSERT_EQ(0, sem_wait(&sem)); +} + +static void* SemWaitThreadFn(void* arg) { + SemWaitThreadTestFn(*reinterpret_cast<sem_t*>(arg)); + return nullptr; +} + +TEST(semaphore, sem_wait__sem_post) { + sem_t s; + ASSERT_EQ(0, sem_init(&s, 0, 0)); + + pthread_t t1, t2, t3; + ASSERT_EQ(0, pthread_create(&t1, NULL, SemWaitThreadFn, &s)); + ASSERT_EQ(0, pthread_create(&t2, NULL, SemWaitThreadFn, &s)); + ASSERT_EQ(0, pthread_create(&t3, NULL, SemWaitThreadFn, &s)); + + ASSERT_EQ(0, sem_post(&s)); + ASSERT_EQ(0, sem_post(&s)); + ASSERT_EQ(0, sem_post(&s)); + + void* result; + ASSERT_EQ(0, pthread_join(t1, &result)); + ASSERT_EQ(0, pthread_join(t2, &result)); + ASSERT_EQ(0, pthread_join(t3, &result)); +} + +static inline void timespec_add_ms(timespec& ts, size_t ms) { + ts.tv_sec += ms / 1000; + ts.tv_nsec += (ms % 1000) * 1000000; + if (ts.tv_nsec >= NS_PER_S) { + ts.tv_sec++; + ts.tv_nsec -= NS_PER_S; + } +} + +TEST(semaphore, sem_timedwait) { + sem_t s; + ASSERT_EQ(0, sem_init(&s, 0, 0)); + + timespec ts; + ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts)); + timespec_add_ms(ts, 100); + + errno = 0; + ASSERT_EQ(-1, sem_timedwait(&s, &ts)); + ASSERT_EQ(ETIMEDOUT, errno); + + // A negative timeout is an error. + errno = 0; + ts.tv_nsec = -1; + ASSERT_EQ(-1, sem_timedwait(&s, &ts)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(0, sem_destroy(&s)); +} + +TEST(semaphore, sem_getvalue) { + sem_t s; + ASSERT_EQ(0, sem_init(&s, 0, 0)); + + int i; + ASSERT_EQ(0, sem_getvalue(&s, &i)); + ASSERT_EQ(0, i); + + ASSERT_EQ(0, sem_post(&s)); + ASSERT_EQ(0, sem_getvalue(&s, &i)); + ASSERT_EQ(1, i); + + ASSERT_EQ(0, sem_post(&s)); + ASSERT_EQ(0, sem_getvalue(&s, &i)); + ASSERT_EQ(2, i); + + ASSERT_EQ(0, sem_wait(&s)); + ASSERT_EQ(0, sem_getvalue(&s, &i)); + ASSERT_EQ(1, i); +} diff --git a/tests/time_test.cpp b/tests/time_test.cpp index c9ead8d..c631b6a 100644 --- a/tests/time_test.cpp +++ b/tests/time_test.cpp @@ -26,6 +26,8 @@ #include "ScopedSignalHandler.h" +#include "private/bionic_constants.h" + TEST(time, gmtime) { time_t t = 0; tm* broken_down = gmtime(&t); @@ -395,7 +397,7 @@ TEST(time, clock_gettime) { ts2.tv_nsec -= ts1.tv_nsec; if (ts2.tv_nsec < 0) { --ts2.tv_sec; - ts2.tv_nsec += 1000000000; + ts2.tv_nsec += NS_PER_S; } // Should be less than (a very generous, to try to avoid flakiness) 1000000ns. |
