Use FUTEX_WAIT_BITSET to avoid converting timeouts.
Add unittests for pthread APIs with timeout parameter. Bug: 17569991 Change-Id: I6b3b9b2feae03680654cd64c3112ce7644632c87
This commit is contained in:
parent
b804b9d67b
commit
c9a659c57b
@ -109,7 +109,7 @@ extern "C" int __cxa_guard_acquire(_guard_t* gv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__futex_wait_ex(&gv->state, false, CONSTRUCTION_UNDERWAY_WITH_WAITER, NULL);
|
__futex_wait_ex(&gv->state, false, CONSTRUCTION_UNDERWAY_WITH_WAITER, false, nullptr);
|
||||||
old_value = atomic_load_explicit(&gv->state, memory_order_relaxed);
|
old_value = atomic_load_explicit(&gv->state, memory_order_relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,18 +52,12 @@ void timeval_from_timespec(timeval& tv, const timespec& ts) {
|
|||||||
tv.tv_usec = ts.tv_nsec / 1000;
|
tv.tv_usec = ts.tv_nsec / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializes 'ts' with the difference between 'abs_ts' and the current time
|
void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts, clockid_t clock) {
|
||||||
// according to 'clock'. Returns false if abstime already expired, true otherwise.
|
clock_gettime(clock, &abs_ts);
|
||||||
bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock) {
|
abs_ts.tv_sec += ts.tv_sec;
|
||||||
clock_gettime(clock, &ts);
|
abs_ts.tv_nsec += ts.tv_nsec;
|
||||||
ts.tv_sec = abs_ts.tv_sec - ts.tv_sec;
|
if (abs_ts.tv_nsec >= NS_PER_S) {
|
||||||
ts.tv_nsec = abs_ts.tv_nsec - ts.tv_nsec;
|
abs_ts.tv_nsec -= NS_PER_S;
|
||||||
if (ts.tv_nsec < 0) {
|
abs_ts.tv_sec++;
|
||||||
ts.tv_sec--;
|
|
||||||
ts.tv_nsec += NS_PER_S;
|
|
||||||
}
|
}
|
||||||
if (ts.tv_nsec < 0 || ts.tv_sec < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ int pthread_barrier_wait(pthread_barrier_t* barrier_interface) {
|
|||||||
// threads have left the barrier. Use acquire operation here to synchronize with
|
// threads have left the barrier. Use acquire operation here to synchronize with
|
||||||
// the last thread leaving the previous cycle, so we can read correct wait_count below.
|
// the last thread leaving the previous cycle, so we can read correct wait_count below.
|
||||||
while(atomic_load_explicit(&barrier->state, memory_order_acquire) == RELEASE) {
|
while(atomic_load_explicit(&barrier->state, memory_order_acquire) == RELEASE) {
|
||||||
__futex_wait_ex(&barrier->state, barrier->pshared, RELEASE, nullptr);
|
__futex_wait_ex(&barrier->state, barrier->pshared, RELEASE, false, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t prev_wait_count = atomic_load_explicit(&barrier->wait_count, memory_order_relaxed);
|
uint32_t prev_wait_count = atomic_load_explicit(&barrier->wait_count, memory_order_relaxed);
|
||||||
@ -152,7 +152,7 @@ int pthread_barrier_wait(pthread_barrier_t* barrier_interface) {
|
|||||||
// Use acquire operation here to synchronize between the last thread entering the
|
// Use acquire operation here to synchronize between the last thread entering the
|
||||||
// barrier with all threads leaving the barrier.
|
// barrier with all threads leaving the barrier.
|
||||||
while (atomic_load_explicit(&barrier->state, memory_order_acquire) == WAIT) {
|
while (atomic_load_explicit(&barrier->state, memory_order_acquire) == WAIT) {
|
||||||
__futex_wait_ex(&barrier->state, barrier->pshared, WAIT, nullptr);
|
__futex_wait_ex(&barrier->state, barrier->pshared, WAIT, false, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Use release operation here to make it not reordered with previous operations.
|
// Use release operation here to make it not reordered with previous operations.
|
||||||
@ -173,7 +173,7 @@ int pthread_barrier_destroy(pthread_barrier_t* barrier_interface) {
|
|||||||
// Use acquire operation here to synchronize with the last thread leaving the barrier.
|
// Use acquire operation here to synchronize with the last thread leaving the barrier.
|
||||||
// So we can read correct wait_count below.
|
// So we can read correct wait_count below.
|
||||||
while (atomic_load_explicit(&barrier->state, memory_order_acquire) == RELEASE) {
|
while (atomic_load_explicit(&barrier->state, memory_order_acquire) == RELEASE) {
|
||||||
__futex_wait_ex(&barrier->state, barrier->pshared, RELEASE, nullptr);
|
__futex_wait_ex(&barrier->state, barrier->pshared, RELEASE, false, nullptr);
|
||||||
}
|
}
|
||||||
if (atomic_load_explicit(&barrier->wait_count, memory_order_relaxed) != 0) {
|
if (atomic_load_explicit(&barrier->wait_count, memory_order_relaxed) != 0) {
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
|
@ -111,8 +111,8 @@ struct pthread_cond_internal_t {
|
|||||||
return COND_IS_SHARED(atomic_load_explicit(&state, memory_order_relaxed));
|
return COND_IS_SHARED(atomic_load_explicit(&state, memory_order_relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_clock() {
|
bool use_realtime_clock() {
|
||||||
return COND_GET_CLOCK(atomic_load_explicit(&state, memory_order_relaxed));
|
return COND_GET_CLOCK(atomic_load_explicit(&state, memory_order_relaxed)) == CLOCK_REALTIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__LP64__)
|
#if defined(__LP64__)
|
||||||
@ -170,12 +170,17 @@ static int __pthread_cond_pulse(pthread_cond_internal_t* cond, int thread_count)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pthread_cond_timedwait_relative(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
|
static int __pthread_cond_timedwait(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
|
||||||
const timespec* rel_timeout_or_null) {
|
bool use_realtime_clock, const timespec* abs_timeout_or_null) {
|
||||||
unsigned int old_state = atomic_load_explicit(&cond->state, memory_order_relaxed);
|
int result = check_timespec(abs_timeout_or_null);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int old_state = atomic_load_explicit(&cond->state, memory_order_relaxed);
|
||||||
pthread_mutex_unlock(mutex);
|
pthread_mutex_unlock(mutex);
|
||||||
int status = __futex_wait_ex(&cond->state, cond->process_shared(), old_state, rel_timeout_or_null);
|
int status = __futex_wait_ex(&cond->state, cond->process_shared(), old_state,
|
||||||
|
use_realtime_clock, abs_timeout_or_null);
|
||||||
pthread_mutex_lock(mutex);
|
pthread_mutex_lock(mutex);
|
||||||
|
|
||||||
if (status == -ETIMEDOUT) {
|
if (status == -ETIMEDOUT) {
|
||||||
@ -184,21 +189,6 @@ static int __pthread_cond_timedwait_relative(pthread_cond_internal_t* cond, pthr
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pthread_cond_timedwait(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
|
|
||||||
const timespec* abs_timeout_or_null, clockid_t clock) {
|
|
||||||
timespec ts;
|
|
||||||
timespec* rel_timeout = NULL;
|
|
||||||
|
|
||||||
if (abs_timeout_or_null != NULL) {
|
|
||||||
rel_timeout = &ts;
|
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return __pthread_cond_timedwait_relative(cond, mutex, rel_timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pthread_cond_broadcast(pthread_cond_t* cond_interface) {
|
int pthread_cond_broadcast(pthread_cond_t* cond_interface) {
|
||||||
return __pthread_cond_pulse(__get_internal_cond(cond_interface), INT_MAX);
|
return __pthread_cond_pulse(__get_internal_cond(cond_interface), INT_MAX);
|
||||||
}
|
}
|
||||||
@ -209,14 +199,14 @@ int pthread_cond_signal(pthread_cond_t* cond_interface) {
|
|||||||
|
|
||||||
int pthread_cond_wait(pthread_cond_t* cond_interface, pthread_mutex_t* mutex) {
|
int pthread_cond_wait(pthread_cond_t* cond_interface, pthread_mutex_t* mutex) {
|
||||||
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
||||||
return __pthread_cond_timedwait(cond, mutex, NULL, cond->get_clock());
|
return __pthread_cond_timedwait(cond, mutex, false, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_cond_timedwait(pthread_cond_t *cond_interface, pthread_mutex_t * mutex,
|
int pthread_cond_timedwait(pthread_cond_t *cond_interface, pthread_mutex_t * mutex,
|
||||||
const timespec *abstime) {
|
const timespec *abstime) {
|
||||||
|
|
||||||
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
|
||||||
return __pthread_cond_timedwait(cond, mutex, abstime, cond->get_clock());
|
return __pthread_cond_timedwait(cond, mutex, cond->use_realtime_clock(), abstime);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__LP64__)
|
#if !defined(__LP64__)
|
||||||
@ -225,8 +215,7 @@ extern "C" int pthread_cond_timedwait_monotonic(pthread_cond_t* cond_interface,
|
|||||||
pthread_mutex_t* mutex,
|
pthread_mutex_t* mutex,
|
||||||
const timespec* abs_timeout) {
|
const timespec* abs_timeout) {
|
||||||
|
|
||||||
return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, abs_timeout,
|
return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, false, abs_timeout);
|
||||||
CLOCK_MONOTONIC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond_interface,
|
extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond_interface,
|
||||||
@ -238,8 +227,13 @@ extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond_interfac
|
|||||||
extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond_interface,
|
extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond_interface,
|
||||||
pthread_mutex_t* mutex,
|
pthread_mutex_t* mutex,
|
||||||
const timespec* rel_timeout) {
|
const timespec* rel_timeout) {
|
||||||
|
timespec ts;
|
||||||
return __pthread_cond_timedwait_relative(__get_internal_cond(cond_interface), mutex, rel_timeout);
|
timespec* abs_timeout = nullptr;
|
||||||
|
if (rel_timeout != nullptr) {
|
||||||
|
absolute_timespec_from_timespec(ts, *rel_timeout, CLOCK_REALTIME);
|
||||||
|
abs_timeout = &ts;
|
||||||
|
}
|
||||||
|
return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, true, abs_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond_interface,
|
extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond_interface,
|
||||||
|
@ -296,11 +296,15 @@ static inline __always_inline int __pthread_normal_mutex_trylock(pthread_mutex_i
|
|||||||
*/
|
*/
|
||||||
static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_internal_t* mutex,
|
static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_internal_t* mutex,
|
||||||
uint16_t shared,
|
uint16_t shared,
|
||||||
const timespec* abs_timeout_or_null,
|
bool use_realtime_clock,
|
||||||
clockid_t clock) {
|
const timespec* abs_timeout_or_null) {
|
||||||
if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) {
|
if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
int result = check_timespec(abs_timeout_or_null);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
ScopedTrace trace("Contending for pthread mutex");
|
ScopedTrace trace("Contending for pthread mutex");
|
||||||
|
|
||||||
@ -317,15 +321,8 @@ static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_inte
|
|||||||
// made by other threads visible to the current CPU.
|
// made by other threads visible to the current CPU.
|
||||||
while (atomic_exchange_explicit(&mutex->state, locked_contended,
|
while (atomic_exchange_explicit(&mutex->state, locked_contended,
|
||||||
memory_order_acquire) != unlocked) {
|
memory_order_acquire) != unlocked) {
|
||||||
timespec ts;
|
if (__futex_wait_ex(&mutex->state, shared, locked_contended, use_realtime_clock,
|
||||||
timespec* rel_timeout = NULL;
|
abs_timeout_or_null) == -ETIMEDOUT) {
|
||||||
if (abs_timeout_or_null != NULL) {
|
|
||||||
rel_timeout = &ts;
|
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (__futex_wait_ex(&mutex->state, shared, locked_contended, rel_timeout) == -ETIMEDOUT) {
|
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,14 +393,15 @@ static inline __always_inline int __recursive_or_errorcheck_mutex_wait(
|
|||||||
pthread_mutex_internal_t* mutex,
|
pthread_mutex_internal_t* mutex,
|
||||||
uint16_t shared,
|
uint16_t shared,
|
||||||
uint16_t old_state,
|
uint16_t old_state,
|
||||||
const timespec* rel_timeout) {
|
bool use_realtime_clock,
|
||||||
|
const timespec* abs_timeout) {
|
||||||
// __futex_wait always waits on a 32-bit value. But state is 16-bit. For a normal mutex, the owner_tid
|
// __futex_wait always waits on a 32-bit value. But state is 16-bit. For a normal mutex, the owner_tid
|
||||||
// field in mutex is not used. On 64-bit devices, the __pad field in mutex is not used.
|
// field in mutex is not used. On 64-bit devices, the __pad field in mutex is not used.
|
||||||
// But when a recursive or errorcheck mutex is used on 32-bit devices, we need to add the
|
// But when a recursive or errorcheck mutex is used on 32-bit devices, we need to add the
|
||||||
// owner_tid value in the value argument for __futex_wait, otherwise we may always get EAGAIN error.
|
// owner_tid value in the value argument for __futex_wait, otherwise we may always get EAGAIN error.
|
||||||
|
|
||||||
#if defined(__LP64__)
|
#if defined(__LP64__)
|
||||||
return __futex_wait_ex(&mutex->state, shared, old_state, rel_timeout);
|
return __futex_wait_ex(&mutex->state, shared, old_state, use_realtime_clock, abs_timeout);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// This implementation works only when the layout of pthread_mutex_internal_t matches below expectation.
|
// This implementation works only when the layout of pthread_mutex_internal_t matches below expectation.
|
||||||
@ -412,19 +410,21 @@ static inline __always_inline int __recursive_or_errorcheck_mutex_wait(
|
|||||||
static_assert(offsetof(pthread_mutex_internal_t, owner_tid) == 2, "");
|
static_assert(offsetof(pthread_mutex_internal_t, owner_tid) == 2, "");
|
||||||
|
|
||||||
uint32_t owner_tid = atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed);
|
uint32_t owner_tid = atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed);
|
||||||
return __futex_wait_ex(&mutex->state, shared, (owner_tid << 16) | old_state, rel_timeout);
|
return __futex_wait_ex(&mutex->state, shared, (owner_tid << 16) | old_state,
|
||||||
|
use_realtime_clock, abs_timeout);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex,
|
static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex,
|
||||||
const timespec* abs_timeout_or_null, clockid_t clock) {
|
bool use_realtime_clock,
|
||||||
|
const timespec* abs_timeout_or_null) {
|
||||||
uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
|
uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
|
||||||
uint16_t mtype = (old_state & MUTEX_TYPE_MASK);
|
uint16_t mtype = (old_state & MUTEX_TYPE_MASK);
|
||||||
uint16_t shared = (old_state & MUTEX_SHARED_MASK);
|
uint16_t shared = (old_state & MUTEX_SHARED_MASK);
|
||||||
|
|
||||||
// Handle common case first.
|
// Handle common case first.
|
||||||
if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) {
|
if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) {
|
||||||
return __pthread_normal_mutex_lock(mutex, shared, abs_timeout_or_null, clock);
|
return __pthread_normal_mutex_lock(mutex, shared, use_realtime_clock, abs_timeout_or_null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we already own this recursive or error-check mutex?
|
// Do we already own this recursive or error-check mutex?
|
||||||
@ -484,16 +484,13 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex,
|
|||||||
old_state = new_state;
|
old_state = new_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are in locked_contended state, sleep until someone wakes us up.
|
int result = check_timespec(abs_timeout_or_null);
|
||||||
timespec ts;
|
if (result != 0) {
|
||||||
timespec* rel_timeout = NULL;
|
return result;
|
||||||
if (abs_timeout_or_null != NULL) {
|
|
||||||
rel_timeout = &ts;
|
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (__recursive_or_errorcheck_mutex_wait(mutex, shared, old_state, rel_timeout) == -ETIMEDOUT) {
|
// We are in locked_contended state, sleep until someone wakes us up.
|
||||||
|
if (__recursive_or_errorcheck_mutex_wait(mutex, shared, old_state, use_realtime_clock,
|
||||||
|
abs_timeout_or_null) == -ETIMEDOUT) {
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
|
old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
|
||||||
@ -518,7 +515,7 @@ int pthread_mutex_lock(pthread_mutex_t* mutex_interface) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return __pthread_mutex_lock_with_timeout(mutex, NULL, 0);
|
return __pthread_mutex_lock_with_timeout(mutex, false, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) {
|
int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) {
|
||||||
@ -613,17 +610,12 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex_interface) {
|
|||||||
|
|
||||||
#if !defined(__LP64__)
|
#if !defined(__LP64__)
|
||||||
extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex_interface, unsigned ms) {
|
extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex_interface, unsigned ms) {
|
||||||
|
timespec ts;
|
||||||
|
timespec_from_ms(ts, ms);
|
||||||
timespec abs_timeout;
|
timespec abs_timeout;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &abs_timeout);
|
absolute_timespec_from_timespec(abs_timeout, ts, CLOCK_MONOTONIC);
|
||||||
abs_timeout.tv_sec += ms / 1000;
|
|
||||||
abs_timeout.tv_nsec += (ms % 1000) * 1000000;
|
|
||||||
if (abs_timeout.tv_nsec >= NS_PER_S) {
|
|
||||||
abs_timeout.tv_sec++;
|
|
||||||
abs_timeout.tv_nsec -= NS_PER_S;
|
|
||||||
}
|
|
||||||
|
|
||||||
int error = __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface),
|
int error = __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface),
|
||||||
&abs_timeout, CLOCK_MONOTONIC);
|
false, &abs_timeout);
|
||||||
if (error == ETIMEDOUT) {
|
if (error == ETIMEDOUT) {
|
||||||
error = EBUSY;
|
error = EBUSY;
|
||||||
}
|
}
|
||||||
@ -633,7 +625,7 @@ extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex_interface, u
|
|||||||
|
|
||||||
int pthread_mutex_timedlock(pthread_mutex_t* mutex_interface, const timespec* abs_timeout) {
|
int pthread_mutex_timedlock(pthread_mutex_t* mutex_interface, const timespec* abs_timeout) {
|
||||||
return __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface),
|
return __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface),
|
||||||
abs_timeout, CLOCK_REALTIME);
|
true, abs_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_mutex_destroy(pthread_mutex_t* mutex_interface) {
|
int pthread_mutex_destroy(pthread_mutex_t* mutex_interface) {
|
||||||
|
@ -79,7 +79,7 @@ int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The initialization is underway, wait for its finish.
|
// The initialization is underway, wait for its finish.
|
||||||
__futex_wait_ex(once_control_ptr, 0, old_value, NULL);
|
__futex_wait_ex(once_control_ptr, 0, old_value, false, nullptr);
|
||||||
old_value = atomic_load_explicit(once_control_ptr, memory_order_acquire);
|
old_value = atomic_load_explicit(once_control_ptr, memory_order_acquire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,9 +294,13 @@ static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int ret = __pthread_rwlock_tryrdlock(rwlock);
|
int result = __pthread_rwlock_tryrdlock(rwlock);
|
||||||
if (ret == 0 || ret == EAGAIN) {
|
if (result == 0 || result == EAGAIN) {
|
||||||
return ret;
|
return result;
|
||||||
|
}
|
||||||
|
result = check_timespec(abs_timeout_or_null);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
@ -304,16 +308,6 @@ static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec ts;
|
|
||||||
timespec* rel_timeout = NULL;
|
|
||||||
|
|
||||||
if (abs_timeout_or_null != NULL) {
|
|
||||||
rel_timeout = &ts;
|
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rwlock->pending_lock.lock();
|
rwlock->pending_lock.lock();
|
||||||
rwlock->pending_reader_count++;
|
rwlock->pending_reader_count++;
|
||||||
|
|
||||||
@ -327,10 +321,10 @@ static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
int old_serial = rwlock->pending_reader_wakeup_serial;
|
int old_serial = rwlock->pending_reader_wakeup_serial;
|
||||||
rwlock->pending_lock.unlock();
|
rwlock->pending_lock.unlock();
|
||||||
|
|
||||||
int futex_ret = 0;
|
int futex_result = 0;
|
||||||
if (!__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
|
if (!__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
|
||||||
futex_ret = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared,
|
futex_result = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared,
|
||||||
old_serial, rel_timeout);
|
old_serial, true, abs_timeout_or_null);
|
||||||
}
|
}
|
||||||
|
|
||||||
rwlock->pending_lock.lock();
|
rwlock->pending_lock.lock();
|
||||||
@ -341,7 +335,7 @@ static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
}
|
}
|
||||||
rwlock->pending_lock.unlock();
|
rwlock->pending_lock.unlock();
|
||||||
|
|
||||||
if (futex_ret == -ETIMEDOUT) {
|
if (futex_result == -ETIMEDOUT) {
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,9 +366,13 @@ static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
int ret = __pthread_rwlock_trywrlock(rwlock);
|
int result = __pthread_rwlock_trywrlock(rwlock);
|
||||||
if (ret == 0) {
|
if (result == 0) {
|
||||||
return ret;
|
return result;
|
||||||
|
}
|
||||||
|
result = check_timespec(abs_timeout_or_null);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
@ -382,16 +380,6 @@ static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec ts;
|
|
||||||
timespec* rel_timeout = NULL;
|
|
||||||
|
|
||||||
if (abs_timeout_or_null != NULL) {
|
|
||||||
rel_timeout = &ts;
|
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rwlock->pending_lock.lock();
|
rwlock->pending_lock.lock();
|
||||||
rwlock->pending_writer_count++;
|
rwlock->pending_writer_count++;
|
||||||
|
|
||||||
@ -401,10 +389,10 @@ static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
int old_serial = rwlock->pending_writer_wakeup_serial;
|
int old_serial = rwlock->pending_writer_wakeup_serial;
|
||||||
rwlock->pending_lock.unlock();
|
rwlock->pending_lock.unlock();
|
||||||
|
|
||||||
int futex_ret = 0;
|
int futex_result = 0;
|
||||||
if (!__can_acquire_write_lock(old_state)) {
|
if (!__can_acquire_write_lock(old_state)) {
|
||||||
futex_ret = __futex_wait_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared,
|
futex_result = __futex_wait_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared,
|
||||||
old_serial, rel_timeout);
|
old_serial, true, abs_timeout_or_null);
|
||||||
}
|
}
|
||||||
|
|
||||||
rwlock->pending_lock.lock();
|
rwlock->pending_lock.lock();
|
||||||
@ -415,7 +403,7 @@ static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
|||||||
}
|
}
|
||||||
rwlock->pending_lock.unlock();
|
rwlock->pending_lock.unlock();
|
||||||
|
|
||||||
if (futex_ret == -ETIMEDOUT) {
|
if (futex_result == -ETIMEDOUT) {
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,7 +415,7 @@ int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock_interface) {
|
|||||||
if (__predict_true(__pthread_rwlock_tryrdlock(rwlock) == 0)) {
|
if (__predict_true(__pthread_rwlock_tryrdlock(rwlock) == 0)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return __pthread_rwlock_timedrdlock(rwlock, NULL);
|
return __pthread_rwlock_timedrdlock(rwlock, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
|
int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
|
||||||
@ -446,7 +434,7 @@ int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_interface) {
|
|||||||
if (__predict_true(__pthread_rwlock_trywrlock(rwlock) == 0)) {
|
if (__predict_true(__pthread_rwlock_trywrlock(rwlock) == 0)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return __pthread_rwlock_timedwrlock(rwlock, NULL);
|
return __pthread_rwlock_timedwrlock(rwlock, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
|
int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
|
||||||
|
@ -220,7 +220,7 @@ int sem_wait(sem_t* sem) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, NULL);
|
__futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, false, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,36 +235,29 @@ int sem_timedwait(sem_t* sem, const timespec* abs_timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check it as per POSIX.
|
// 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) {
|
int result = check_timespec(abs_timeout);
|
||||||
errno = EINVAL;
|
if (result != 0) {
|
||||||
|
errno = result;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int shared = SEM_GET_SHARED(sem_count_ptr);
|
unsigned int shared = SEM_GET_SHARED(sem_count_ptr);
|
||||||
|
|
||||||
while (true) {
|
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.
|
// Try to grab the semaphore. If the value was 0, this will also change it to -1.
|
||||||
if (__sem_dec(sem_count_ptr) > 0) {
|
if (__sem_dec(sem_count_ptr) > 0) {
|
||||||
break;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contention detected. Wait for a wakeup event.
|
// Contention detected. Wait for a wakeup event.
|
||||||
int ret = __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, &ts);
|
int result = __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, true, abs_timeout);
|
||||||
|
|
||||||
// Return in case of timeout or interrupt.
|
// Return in case of timeout or interrupt.
|
||||||
if (ret == -ETIMEDOUT || ret == -EINTR) {
|
if (result == -ETIMEDOUT || result == -EINTR) {
|
||||||
errno = -ret;
|
errno = -result;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sem_post(sem_t* sem) {
|
int sem_post(sem_t* sem) {
|
||||||
|
@ -40,10 +40,12 @@ __BEGIN_DECLS
|
|||||||
|
|
||||||
struct timespec;
|
struct timespec;
|
||||||
|
|
||||||
static inline __always_inline int __futex(volatile void* ftx, int op, int value, const struct timespec* timeout) {
|
static inline __always_inline int __futex(volatile void* ftx, int op, int value,
|
||||||
|
const struct timespec* timeout,
|
||||||
|
int bitset) {
|
||||||
// Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to.
|
// Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to.
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
int result = syscall(__NR_futex, ftx, op, value, timeout);
|
int result = syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset);
|
||||||
if (__predict_false(result == -1)) {
|
if (__predict_false(result == -1)) {
|
||||||
result = -errno;
|
result = -errno;
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
@ -52,19 +54,22 @@ static inline __always_inline int __futex(volatile void* ftx, int op, int value,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline int __futex_wake(volatile void* ftx, int count) {
|
static inline int __futex_wake(volatile void* ftx, int count) {
|
||||||
return __futex(ftx, FUTEX_WAKE, count, NULL);
|
return __futex(ftx, FUTEX_WAKE, count, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __futex_wake_ex(volatile void* ftx, bool shared, int count) {
|
static inline int __futex_wake_ex(volatile void* ftx, bool shared, int count) {
|
||||||
return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL);
|
return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
|
static inline int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
|
||||||
return __futex(ftx, FUTEX_WAIT, value, timeout);
|
return __futex(ftx, FUTEX_WAIT, value, timeout, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value, const struct timespec* timeout) {
|
static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value,
|
||||||
return __futex(ftx, shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE, value, timeout);
|
bool use_realtime_clock, const struct timespec* abs_timeout) {
|
||||||
|
return __futex(ftx, (shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE) |
|
||||||
|
(use_realtime_clock ? FUTEX_CLOCK_REALTIME : 0), value, abs_timeout,
|
||||||
|
FUTEX_BITSET_MATCH_ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
@ -57,7 +57,7 @@ class Lock {
|
|||||||
}
|
}
|
||||||
while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) {
|
while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) {
|
||||||
// TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping.
|
// TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping.
|
||||||
__futex_wait_ex(&state, process_shared, LockedWithWaiter, NULL);
|
__futex_wait_ex(&state, process_shared, LockedWithWaiter, false, nullptr);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,12 @@
|
|||||||
#ifndef _BIONIC_TIME_CONVERSIONS_H
|
#ifndef _BIONIC_TIME_CONVERSIONS_H
|
||||||
#define _BIONIC_TIME_CONVERSIONS_H
|
#define _BIONIC_TIME_CONVERSIONS_H
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
#include "private/bionic_constants.h"
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
__LIBC_HIDDEN__ bool timespec_from_timeval(timespec& ts, const timeval& tv);
|
__LIBC_HIDDEN__ bool timespec_from_timeval(timespec& ts, const timeval& tv);
|
||||||
@ -39,8 +42,21 @@ __LIBC_HIDDEN__ void timespec_from_ms(timespec& ts, const int ms);
|
|||||||
|
|
||||||
__LIBC_HIDDEN__ void timeval_from_timespec(timeval& tv, const timespec& ts);
|
__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);
|
__LIBC_HIDDEN__ void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts,
|
||||||
|
clockid_t clock);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
|
static inline int check_timespec(const timespec* ts) {
|
||||||
|
if (ts != nullptr) {
|
||||||
|
if (ts->tv_nsec < 0 || ts->tv_nsec >= NS_PER_S) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (ts->tv_sec < 0) {
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <base/file.h>
|
#include <base/file.h>
|
||||||
#include <base/stringprintf.h>
|
#include <base/stringprintf.h>
|
||||||
|
|
||||||
|
#include "private/bionic_constants.h"
|
||||||
#include "private/bionic_macros.h"
|
#include "private/bionic_macros.h"
|
||||||
#include "private/ScopeGuard.h"
|
#include "private/ScopeGuard.h"
|
||||||
#include "BionicDeathTest.h"
|
#include "BionicDeathTest.h"
|
||||||
@ -744,35 +745,41 @@ struct RwlockWakeupHelperArg {
|
|||||||
LOCK_INITIALIZED,
|
LOCK_INITIALIZED,
|
||||||
LOCK_WAITING,
|
LOCK_WAITING,
|
||||||
LOCK_RELEASED,
|
LOCK_RELEASED,
|
||||||
LOCK_ACCESSED
|
LOCK_ACCESSED,
|
||||||
|
LOCK_TIMEDOUT,
|
||||||
};
|
};
|
||||||
std::atomic<Progress> progress;
|
std::atomic<Progress> progress;
|
||||||
std::atomic<pid_t> tid;
|
std::atomic<pid_t> tid;
|
||||||
|
std::function<int (pthread_rwlock_t*)> trylock_function;
|
||||||
|
std::function<int (pthread_rwlock_t*)> lock_function;
|
||||||
|
std::function<int (pthread_rwlock_t*, const timespec*)> timed_lock_function;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pthread_rwlock_reader_wakeup_writer_helper(RwlockWakeupHelperArg* arg) {
|
static void pthread_rwlock_wakeup_helper(RwlockWakeupHelperArg* arg) {
|
||||||
arg->tid = gettid();
|
arg->tid = gettid();
|
||||||
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
|
||||||
arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
|
arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
|
||||||
|
|
||||||
ASSERT_EQ(EBUSY, pthread_rwlock_trywrlock(&arg->lock));
|
ASSERT_EQ(EBUSY, arg->trylock_function(&arg->lock));
|
||||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&arg->lock));
|
ASSERT_EQ(0, arg->lock_function(&arg->lock));
|
||||||
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_RELEASED, arg->progress);
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_RELEASED, arg->progress);
|
||||||
ASSERT_EQ(0, pthread_rwlock_unlock(&arg->lock));
|
ASSERT_EQ(0, pthread_rwlock_unlock(&arg->lock));
|
||||||
|
|
||||||
arg->progress = RwlockWakeupHelperArg::LOCK_ACCESSED;
|
arg->progress = RwlockWakeupHelperArg::LOCK_ACCESSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread, pthread_rwlock_reader_wakeup_writer) {
|
static void test_pthread_rwlock_reader_wakeup_writer(std::function<int (pthread_rwlock_t*)> lock_function) {
|
||||||
RwlockWakeupHelperArg wakeup_arg;
|
RwlockWakeupHelperArg wakeup_arg;
|
||||||
ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL));
|
ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL));
|
||||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&wakeup_arg.lock));
|
ASSERT_EQ(0, pthread_rwlock_rdlock(&wakeup_arg.lock));
|
||||||
wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
|
wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
|
||||||
wakeup_arg.tid = 0;
|
wakeup_arg.tid = 0;
|
||||||
|
wakeup_arg.trylock_function = pthread_rwlock_trywrlock;
|
||||||
|
wakeup_arg.lock_function = lock_function;
|
||||||
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
ASSERT_EQ(0, pthread_create(&thread, NULL,
|
ASSERT_EQ(0, pthread_create(&thread, NULL,
|
||||||
reinterpret_cast<void* (*)(void*)>(pthread_rwlock_reader_wakeup_writer_helper), &wakeup_arg));
|
reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_helper), &wakeup_arg));
|
||||||
WaitUntilThreadSleep(wakeup_arg.tid);
|
WaitUntilThreadSleep(wakeup_arg.tid);
|
||||||
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
|
||||||
|
|
||||||
@ -784,29 +791,31 @@ TEST(pthread, pthread_rwlock_reader_wakeup_writer) {
|
|||||||
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pthread_rwlock_writer_wakeup_reader_helper(RwlockWakeupHelperArg* arg) {
|
TEST(pthread, pthread_rwlock_reader_wakeup_writer) {
|
||||||
arg->tid = gettid();
|
test_pthread_rwlock_reader_wakeup_writer(pthread_rwlock_wrlock);
|
||||||
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
|
|
||||||
arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
|
|
||||||
|
|
||||||
ASSERT_EQ(EBUSY, pthread_rwlock_tryrdlock(&arg->lock));
|
|
||||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&arg->lock));
|
|
||||||
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_RELEASED, arg->progress);
|
|
||||||
ASSERT_EQ(0, pthread_rwlock_unlock(&arg->lock));
|
|
||||||
|
|
||||||
arg->progress = RwlockWakeupHelperArg::LOCK_ACCESSED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread, pthread_rwlock_writer_wakeup_reader) {
|
TEST(pthread, pthread_rwlock_reader_wakeup_writer_timedwait) {
|
||||||
|
timespec ts;
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
|
ts.tv_sec += 1;
|
||||||
|
test_pthread_rwlock_reader_wakeup_writer([&](pthread_rwlock_t* lock) {
|
||||||
|
return pthread_rwlock_timedwrlock(lock, &ts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pthread_rwlock_writer_wakeup_reader(std::function<int (pthread_rwlock_t*)> lock_function) {
|
||||||
RwlockWakeupHelperArg wakeup_arg;
|
RwlockWakeupHelperArg wakeup_arg;
|
||||||
ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL));
|
ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL));
|
||||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&wakeup_arg.lock));
|
ASSERT_EQ(0, pthread_rwlock_wrlock(&wakeup_arg.lock));
|
||||||
wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
|
wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
|
||||||
wakeup_arg.tid = 0;
|
wakeup_arg.tid = 0;
|
||||||
|
wakeup_arg.trylock_function = pthread_rwlock_tryrdlock;
|
||||||
|
wakeup_arg.lock_function = lock_function;
|
||||||
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
ASSERT_EQ(0, pthread_create(&thread, NULL,
|
ASSERT_EQ(0, pthread_create(&thread, NULL,
|
||||||
reinterpret_cast<void* (*)(void*)>(pthread_rwlock_writer_wakeup_reader_helper), &wakeup_arg));
|
reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_helper), &wakeup_arg));
|
||||||
WaitUntilThreadSleep(wakeup_arg.tid);
|
WaitUntilThreadSleep(wakeup_arg.tid);
|
||||||
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
|
||||||
|
|
||||||
@ -818,6 +827,85 @@ TEST(pthread, pthread_rwlock_writer_wakeup_reader) {
|
|||||||
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(pthread, pthread_rwlock_writer_wakeup_reader) {
|
||||||
|
test_pthread_rwlock_writer_wakeup_reader(pthread_rwlock_rdlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread, pthread_rwlock_writer_wakeup_reader_timedwait) {
|
||||||
|
timespec ts;
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
|
ts.tv_sec += 1;
|
||||||
|
test_pthread_rwlock_writer_wakeup_reader([&](pthread_rwlock_t* lock) {
|
||||||
|
return pthread_rwlock_timedrdlock(lock, &ts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pthread_rwlock_wakeup_timeout_helper(RwlockWakeupHelperArg* arg) {
|
||||||
|
arg->tid = gettid();
|
||||||
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
|
||||||
|
arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
|
||||||
|
|
||||||
|
ASSERT_EQ(EBUSY, arg->trylock_function(&arg->lock));
|
||||||
|
|
||||||
|
timespec ts;
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
|
ASSERT_EQ(ETIMEDOUT, arg->timed_lock_function(&arg->lock, &ts));
|
||||||
|
ts.tv_nsec = -1;
|
||||||
|
ASSERT_EQ(EINVAL, arg->timed_lock_function(&arg->lock, &ts));
|
||||||
|
ts.tv_nsec = NS_PER_S;
|
||||||
|
ASSERT_EQ(EINVAL, arg->timed_lock_function(&arg->lock, &ts));
|
||||||
|
ts.tv_nsec = NS_PER_S - 1;
|
||||||
|
ts.tv_sec = -1;
|
||||||
|
ASSERT_EQ(ETIMEDOUT, arg->timed_lock_function(&arg->lock, &ts));
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
|
ts.tv_sec += 1;
|
||||||
|
ASSERT_EQ(ETIMEDOUT, arg->timed_lock_function(&arg->lock, &ts));
|
||||||
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, arg->progress);
|
||||||
|
arg->progress = RwlockWakeupHelperArg::LOCK_TIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread, pthread_rwlock_timedrdlock_timeout) {
|
||||||
|
RwlockWakeupHelperArg wakeup_arg;
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, nullptr));
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_wrlock(&wakeup_arg.lock));
|
||||||
|
wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
|
||||||
|
wakeup_arg.tid = 0;
|
||||||
|
wakeup_arg.trylock_function = pthread_rwlock_tryrdlock;
|
||||||
|
wakeup_arg.timed_lock_function = pthread_rwlock_timedrdlock;
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
ASSERT_EQ(0, pthread_create(&thread, nullptr,
|
||||||
|
reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_timeout_helper), &wakeup_arg));
|
||||||
|
WaitUntilThreadSleep(wakeup_arg.tid);
|
||||||
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, pthread_join(thread, nullptr));
|
||||||
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_TIMEDOUT, wakeup_arg.progress);
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock));
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread, pthread_rwlock_timedwrlock_timeout) {
|
||||||
|
RwlockWakeupHelperArg wakeup_arg;
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, nullptr));
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_rdlock(&wakeup_arg.lock));
|
||||||
|
wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
|
||||||
|
wakeup_arg.tid = 0;
|
||||||
|
wakeup_arg.trylock_function = pthread_rwlock_trywrlock;
|
||||||
|
wakeup_arg.timed_lock_function = pthread_rwlock_timedwrlock;
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
ASSERT_EQ(0, pthread_create(&thread, nullptr,
|
||||||
|
reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_timeout_helper), &wakeup_arg));
|
||||||
|
WaitUntilThreadSleep(wakeup_arg.tid);
|
||||||
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, pthread_join(thread, nullptr));
|
||||||
|
ASSERT_EQ(RwlockWakeupHelperArg::LOCK_TIMEDOUT, wakeup_arg.progress);
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock));
|
||||||
|
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
||||||
|
}
|
||||||
|
|
||||||
class RwlockKindTestHelper {
|
class RwlockKindTestHelper {
|
||||||
private:
|
private:
|
||||||
struct ThreadArg {
|
struct ThreadArg {
|
||||||
@ -1062,36 +1150,44 @@ class pthread_CondWakeupTest : public ::testing::Test {
|
|||||||
};
|
};
|
||||||
std::atomic<Progress> progress;
|
std::atomic<Progress> progress;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
std::function<int (pthread_cond_t* cond, pthread_mutex_t* mutex)> wait_function;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void SetUp() {
|
void SetUp() override {
|
||||||
ASSERT_EQ(0, pthread_mutex_init(&mutex, NULL));
|
ASSERT_EQ(0, pthread_mutex_init(&mutex, nullptr));
|
||||||
ASSERT_EQ(0, pthread_cond_init(&cond, NULL));
|
}
|
||||||
|
|
||||||
|
void InitCond(clockid_t clock=CLOCK_REALTIME) {
|
||||||
|
pthread_condattr_t attr;
|
||||||
|
ASSERT_EQ(0, pthread_condattr_init(&attr));
|
||||||
|
ASSERT_EQ(0, pthread_condattr_setclock(&attr, clock));
|
||||||
|
ASSERT_EQ(0, pthread_cond_init(&cond, &attr));
|
||||||
|
ASSERT_EQ(0, pthread_condattr_destroy(&attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartWaitingThread(std::function<int (pthread_cond_t* cond, pthread_mutex_t* mutex)> wait_function) {
|
||||||
progress = INITIALIZED;
|
progress = INITIALIZED;
|
||||||
ASSERT_EQ(0,
|
this->wait_function = wait_function;
|
||||||
pthread_create(&thread, NULL, reinterpret_cast<void* (*)(void*)>(WaitThreadFn), this));
|
ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast<void* (*)(void*)>(WaitThreadFn), this));
|
||||||
}
|
while (progress != WAITING) {
|
||||||
|
|
||||||
virtual void TearDown() {
|
|
||||||
ASSERT_EQ(0, pthread_join(thread, NULL));
|
|
||||||
ASSERT_EQ(FINISHED, progress);
|
|
||||||
ASSERT_EQ(0, pthread_cond_destroy(&cond));
|
|
||||||
ASSERT_EQ(0, pthread_mutex_destroy(&mutex));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SleepUntilProgress(Progress expected_progress) {
|
|
||||||
while (progress != expected_progress) {
|
|
||||||
usleep(5000);
|
usleep(5000);
|
||||||
}
|
}
|
||||||
usleep(5000);
|
usleep(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
ASSERT_EQ(0, pthread_join(thread, nullptr));
|
||||||
|
ASSERT_EQ(FINISHED, progress);
|
||||||
|
ASSERT_EQ(0, pthread_cond_destroy(&cond));
|
||||||
|
ASSERT_EQ(0, pthread_mutex_destroy(&mutex));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void WaitThreadFn(pthread_CondWakeupTest* test) {
|
static void WaitThreadFn(pthread_CondWakeupTest* test) {
|
||||||
ASSERT_EQ(0, pthread_mutex_lock(&test->mutex));
|
ASSERT_EQ(0, pthread_mutex_lock(&test->mutex));
|
||||||
test->progress = WAITING;
|
test->progress = WAITING;
|
||||||
while (test->progress == WAITING) {
|
while (test->progress == WAITING) {
|
||||||
ASSERT_EQ(0, pthread_cond_wait(&test->cond, &test->mutex));
|
ASSERT_EQ(0, test->wait_function(&test->cond, &test->mutex));
|
||||||
}
|
}
|
||||||
ASSERT_EQ(SIGNALED, test->progress);
|
ASSERT_EQ(SIGNALED, test->progress);
|
||||||
test->progress = FINISHED;
|
test->progress = FINISHED;
|
||||||
@ -1099,39 +1195,65 @@ class pthread_CondWakeupTest : public ::testing::Test {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(pthread_CondWakeupTest, signal) {
|
TEST_F(pthread_CondWakeupTest, signal_wait) {
|
||||||
SleepUntilProgress(WAITING);
|
InitCond();
|
||||||
|
StartWaitingThread([](pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
||||||
|
return pthread_cond_wait(cond, mutex);
|
||||||
|
});
|
||||||
progress = SIGNALED;
|
progress = SIGNALED;
|
||||||
pthread_cond_signal(&cond);
|
ASSERT_EQ(0, pthread_cond_signal(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(pthread_CondWakeupTest, broadcast) {
|
TEST_F(pthread_CondWakeupTest, broadcast_wait) {
|
||||||
SleepUntilProgress(WAITING);
|
InitCond();
|
||||||
|
StartWaitingThread([](pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
||||||
|
return pthread_cond_wait(cond, mutex);
|
||||||
|
});
|
||||||
progress = SIGNALED;
|
progress = SIGNALED;
|
||||||
pthread_cond_broadcast(&cond);
|
ASSERT_EQ(0, pthread_cond_broadcast(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread, pthread_mutex_timedlock) {
|
TEST_F(pthread_CondWakeupTest, signal_timedwait_CLOCK_REALTIME) {
|
||||||
pthread_mutex_t m;
|
InitCond(CLOCK_REALTIME);
|
||||||
ASSERT_EQ(0, pthread_mutex_init(&m, NULL));
|
|
||||||
|
|
||||||
// If the mutex is already locked, pthread_mutex_timedlock should time out.
|
|
||||||
ASSERT_EQ(0, pthread_mutex_lock(&m));
|
|
||||||
|
|
||||||
timespec ts;
|
timespec ts;
|
||||||
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
ts.tv_nsec += 1;
|
ts.tv_sec += 1;
|
||||||
ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
|
StartWaitingThread([&](pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
||||||
|
return pthread_cond_timedwait(cond, mutex, &ts);
|
||||||
|
});
|
||||||
|
progress = SIGNALED;
|
||||||
|
ASSERT_EQ(0, pthread_cond_signal(&cond));
|
||||||
|
}
|
||||||
|
|
||||||
// If the mutex is unlocked, pthread_mutex_timedlock should succeed.
|
TEST_F(pthread_CondWakeupTest, signal_timedwait_CLOCK_MONOTONIC) {
|
||||||
ASSERT_EQ(0, pthread_mutex_unlock(&m));
|
InitCond(CLOCK_MONOTONIC);
|
||||||
|
timespec ts;
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &ts));
|
||||||
|
ts.tv_sec += 1;
|
||||||
|
StartWaitingThread([&](pthread_cond_t* cond, pthread_mutex_t* mutex) {
|
||||||
|
return pthread_cond_timedwait(cond, mutex, &ts);
|
||||||
|
});
|
||||||
|
progress = SIGNALED;
|
||||||
|
ASSERT_EQ(0, pthread_cond_signal(&cond));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pthread, pthread_cond_timedwait_timeout) {
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
ASSERT_EQ(0, pthread_mutex_init(&mutex, nullptr));
|
||||||
|
pthread_cond_t cond;
|
||||||
|
ASSERT_EQ(0, pthread_cond_init(&cond, nullptr));
|
||||||
|
ASSERT_EQ(0, pthread_mutex_lock(&mutex));
|
||||||
|
timespec ts;
|
||||||
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
ts.tv_nsec += 1;
|
ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&cond, &mutex, &ts));
|
||||||
ASSERT_EQ(0, pthread_mutex_timedlock(&m, &ts));
|
ts.tv_nsec = -1;
|
||||||
|
ASSERT_EQ(EINVAL, pthread_cond_timedwait(&cond, &mutex, &ts));
|
||||||
ASSERT_EQ(0, pthread_mutex_unlock(&m));
|
ts.tv_nsec = NS_PER_S;
|
||||||
ASSERT_EQ(0, pthread_mutex_destroy(&m));
|
ASSERT_EQ(EINVAL, pthread_cond_timedwait(&cond, &mutex, &ts));
|
||||||
|
ts.tv_nsec = NS_PER_S - 1;
|
||||||
|
ts.tv_sec = -1;
|
||||||
|
ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&cond, &mutex, &ts));
|
||||||
|
ASSERT_EQ(0, pthread_mutex_unlock(&mutex));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread, pthread_attr_getstack__main_thread) {
|
TEST(pthread, pthread_attr_getstack__main_thread) {
|
||||||
@ -1552,6 +1674,35 @@ TEST(pthread, pthread_mutex_owner_tid_limit) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(pthread, pthread_mutex_timedlock) {
|
||||||
|
pthread_mutex_t m;
|
||||||
|
ASSERT_EQ(0, pthread_mutex_init(&m, nullptr));
|
||||||
|
|
||||||
|
// If the mutex is already locked, pthread_mutex_timedlock should time out.
|
||||||
|
ASSERT_EQ(0, pthread_mutex_lock(&m));
|
||||||
|
|
||||||
|
timespec ts;
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
|
ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
|
||||||
|
ts.tv_nsec = -1;
|
||||||
|
ASSERT_EQ(EINVAL, pthread_mutex_timedlock(&m, &ts));
|
||||||
|
ts.tv_nsec = NS_PER_S;
|
||||||
|
ASSERT_EQ(EINVAL, pthread_mutex_timedlock(&m, &ts));
|
||||||
|
ts.tv_nsec = NS_PER_S - 1;
|
||||||
|
ts.tv_sec = -1;
|
||||||
|
ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
|
||||||
|
|
||||||
|
// If the mutex is unlocked, pthread_mutex_timedlock should succeed.
|
||||||
|
ASSERT_EQ(0, pthread_mutex_unlock(&m));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||||
|
ts.tv_sec += 1;
|
||||||
|
ASSERT_EQ(0, pthread_mutex_timedlock(&m, &ts));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, pthread_mutex_unlock(&m));
|
||||||
|
ASSERT_EQ(0, pthread_mutex_destroy(&m));
|
||||||
|
}
|
||||||
|
|
||||||
class StrictAlignmentAllocator {
|
class StrictAlignmentAllocator {
|
||||||
public:
|
public:
|
||||||
void* allocate(size_t size, size_t alignment) {
|
void* allocate(size_t size, size_t alignment) {
|
||||||
@ -1749,13 +1900,13 @@ void BarrierOrderingTestHelper(BarrierOrderingTestHelperArg* arg) {
|
|||||||
const size_t ITERATION_COUNT = 10000;
|
const size_t ITERATION_COUNT = 10000;
|
||||||
for (size_t i = 1; i <= ITERATION_COUNT; ++i) {
|
for (size_t i = 1; i <= ITERATION_COUNT; ++i) {
|
||||||
arg->array[arg->id] = i;
|
arg->array[arg->id] = i;
|
||||||
int ret = pthread_barrier_wait(arg->barrier);
|
int result = pthread_barrier_wait(arg->barrier);
|
||||||
ASSERT_TRUE(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
|
ASSERT_TRUE(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);
|
||||||
for (size_t j = 0; j < arg->array_length; ++j) {
|
for (size_t j = 0; j < arg->array_length; ++j) {
|
||||||
ASSERT_EQ(i, arg->array[j]);
|
ASSERT_EQ(i, arg->array[j]);
|
||||||
}
|
}
|
||||||
ret = pthread_barrier_wait(arg->barrier);
|
result = pthread_barrier_wait(arg->barrier);
|
||||||
ASSERT_TRUE(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
|
ASSERT_TRUE(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +117,16 @@ TEST(semaphore, sem_timedwait) {
|
|||||||
ts.tv_nsec = -1;
|
ts.tv_nsec = -1;
|
||||||
ASSERT_EQ(-1, sem_timedwait(&s, &ts));
|
ASSERT_EQ(-1, sem_timedwait(&s, &ts));
|
||||||
ASSERT_EQ(EINVAL, errno);
|
ASSERT_EQ(EINVAL, errno);
|
||||||
|
errno = 0;
|
||||||
|
ts.tv_nsec = NS_PER_S;
|
||||||
|
ASSERT_EQ(-1, sem_timedwait(&s, &ts));
|
||||||
|
ASSERT_EQ(EINVAL, errno);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
ts.tv_nsec = NS_PER_S - 1;
|
||||||
|
ts.tv_sec = -1;
|
||||||
|
ASSERT_EQ(-1, sem_timedwait(&s, &ts));
|
||||||
|
ASSERT_EQ(ETIMEDOUT, errno);
|
||||||
|
|
||||||
ASSERT_EQ(0, sem_destroy(&s));
|
ASSERT_EQ(0, sem_destroy(&s));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user