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:
parent
adc01348ee
commit
04303f5a8a
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
322
libc/bionic/semaphore.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
||||
|
22
libc/private/bionic_constants.h
Normal file
22
libc/private/bionic_constants.h
Normal 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_
|
@ -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
|
||||
|
@ -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
143
tests/semaphore_test.cpp
Normal 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);
|
||||
}
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user