Add semaphore tests, fix sem_destroy.

Bug: https://code.google.com/p/android/issues/detail?id=76088
Change-Id: I4a0561b23e90312384d40a1c804ca64ee98f4066
This commit is contained in:
Elliott Hughes 2014-09-18 16:11:59 -07:00
parent adc01348ee
commit 04303f5a8a
17 changed files with 567 additions and 550 deletions

View File

@ -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 \

View File

@ -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;
}

View File

@ -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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

322
libc/bionic/semaphore.cpp Normal file
View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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_ */

View File

@ -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);
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*);
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);
/* These aren't actually implemented. */
sem_t* sem_open(const char*, int, ...);
int sem_close(sem_t*);
int sem_unlink(const char*);
__END_DECLS

View File

@ -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_

View File

@ -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

View File

@ -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 \

143
tests/semaphore_test.cpp Normal file
View File

@ -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);
}

View File

@ -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.