[libc] C11 threads: Add cnd_timedwait and mtx_trylock. (#195966)
Use recently added support for timed_wait in CndVar and try_lock in Mutex to implement the C11 functions `cnd_timedwait` and `mtx_trylock` (counterparts of `pthread_cond_timedwait` and `pthread_mutex_trylock`, respectively). Fixes a minor edge case in `ensure_monotonicity` conversion where the converted timeout could fall before epoch - clamp it to be 0 / epoch in that case. Assisted by: Gemini
This commit is contained in:
1 parent
36f60d8486
commit
2204247150
17 files changed
+294
-7
No files matched your search
@@ -1195,10 +1195,12 @@ if(LLVM_LIBC_FULL_BUILD)
|
||||
libc.src.threads.cnd_destroy
|
||||
libc.src.threads.cnd_init
|
||||
libc.src.threads.cnd_signal
|
||||
libc.src.threads.cnd_timedwait
|
||||
libc.src.threads.cnd_wait
|
||||
libc.src.threads.mtx_destroy
|
||||
libc.src.threads.mtx_init
|
||||
libc.src.threads.mtx_lock
|
||||
libc.src.threads.mtx_trylock
|
||||
libc.src.threads.mtx_unlock
|
||||
libc.src.threads.thrd_create
|
||||
libc.src.threads.thrd_current
|
||||
|
||||
@@ -1318,10 +1318,12 @@ if(LLVM_LIBC_FULL_BUILD)
|
||||
libc.src.threads.cnd_destroy
|
||||
libc.src.threads.cnd_init
|
||||
libc.src.threads.cnd_signal
|
||||
libc.src.threads.cnd_timedwait
|
||||
libc.src.threads.cnd_wait
|
||||
libc.src.threads.mtx_destroy
|
||||
libc.src.threads.mtx_init
|
||||
libc.src.threads.mtx_lock
|
||||
libc.src.threads.mtx_trylock
|
||||
libc.src.threads.mtx_unlock
|
||||
libc.src.threads.thrd_create
|
||||
libc.src.threads.thrd_current
|
||||
|
||||
@@ -1392,10 +1392,12 @@ if(LLVM_LIBC_FULL_BUILD)
|
||||
libc.src.threads.cnd_destroy
|
||||
libc.src.threads.cnd_init
|
||||
libc.src.threads.cnd_signal
|
||||
libc.src.threads.cnd_timedwait
|
||||
libc.src.threads.cnd_wait
|
||||
libc.src.threads.mtx_destroy
|
||||
libc.src.threads.mtx_init
|
||||
libc.src.threads.mtx_lock
|
||||
libc.src.threads.mtx_trylock
|
||||
libc.src.threads.mtx_unlock
|
||||
libc.src.threads.thrd_create
|
||||
libc.src.threads.thrd_current
|
||||
|
||||
@@ -340,6 +340,7 @@ add_header_macro(
|
||||
.llvm-libc-types.thrd_start_t
|
||||
.llvm-libc-types.tss_t
|
||||
.llvm-libc-types.tss_dtor_t
|
||||
.llvm-libc-types.struct_timespec
|
||||
)
|
||||
|
||||
add_header_macro(
|
||||
|
||||
@@ -13,6 +13,7 @@ types:
|
||||
- type_name: thrd_t
|
||||
- type_name: tss_t
|
||||
- type_name: tss_dtor_t
|
||||
- type_name: struct_timespec
|
||||
enums:
|
||||
- name: mtx_plain
|
||||
value: null
|
||||
@@ -63,6 +64,14 @@ functions:
|
||||
return_type: int
|
||||
arguments:
|
||||
- type: cnd_t *
|
||||
- name: cnd_timedwait
|
||||
standards:
|
||||
- stdc
|
||||
return_type: int
|
||||
arguments:
|
||||
- type: cnd_t *__restrict
|
||||
- type: mtx_t *__restrict
|
||||
- type: const struct timespec *__restrict
|
||||
- name: cnd_wait
|
||||
standards:
|
||||
- stdc
|
||||
@@ -89,6 +98,12 @@ functions:
|
||||
return_type: int
|
||||
arguments:
|
||||
- type: mtx_t *
|
||||
- name: mtx_trylock
|
||||
standards:
|
||||
- stdc
|
||||
return_type: int
|
||||
arguments:
|
||||
- type: mtx_t *
|
||||
- name: mtx_unlock
|
||||
standards:
|
||||
- stdc
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
// Mutex with non-static methods having the following signature:
|
||||
//
|
||||
// MutexError lock();
|
||||
// MutexError trylock();
|
||||
// MutexError try_lock();
|
||||
// MutexError timed_lock(...);
|
||||
// MutexError unlock();
|
||||
// MutexError reset(); // Used to reset inconsistent robust mutexes.
|
||||
@@ -61,7 +61,7 @@ struct Mutex {
|
||||
LIBC_INLINE MutexError lock() { return MutexError::NONE; }
|
||||
LIBC_INLINE MutexError unlock() { return MutexError::NONE; }
|
||||
LIBC_INLINE MutexError reset() { return MutexError::NONE; }
|
||||
LIBC_INLINE MutexError trylock() { return MutexError::NONE; }
|
||||
LIBC_INLINE MutexError try_lock() { return MutexError::NONE; }
|
||||
LIBC_INLINE bool is_robust() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ LIBC_INLINE timespec convert_clock(timespec input, clockid_t from,
|
||||
output.tv_sec = input.tv_sec - from_time.tv_sec + to_time.tv_sec;
|
||||
output.tv_nsec = input.tv_nsec - from_time.tv_nsec + to_time.tv_nsec;
|
||||
|
||||
if (output.tv_nsec > 1_s_ns) {
|
||||
if (output.tv_nsec >= 1_s_ns) {
|
||||
output.tv_sec++;
|
||||
output.tv_nsec -= 1_s_ns;
|
||||
} else if (output.tv_nsec < 0) {
|
||||
|
||||
@@ -31,6 +31,10 @@ LIBC_INLINE void ensure_monotonicity(AbsTimeout &timeout) {
|
||||
convert_clock(timeout.get_timespec(), CLOCK_REALTIME, CLOCK_MONOTONIC),
|
||||
false);
|
||||
|
||||
// Clamp the timeout to epoch if becomes negative after the conversion.
|
||||
if (!res.has_value() && res.error() == AbsTimeout::Error::BeforeEpoch)
|
||||
res = AbsTimeout::from_timespec(timespec{0, 0}, false);
|
||||
|
||||
LIBC_ASSERT(res.has_value());
|
||||
if (!res.has_value())
|
||||
__builtin_unreachable();
|
||||
|
||||
@@ -117,6 +117,17 @@ add_entrypoint_object(
|
||||
libc.src.__support.threads.mutex
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
mtx_trylock
|
||||
SRCS
|
||||
mtx_trylock.cpp
|
||||
HDRS
|
||||
mtx_trylock.h
|
||||
DEPENDS
|
||||
libc.include.threads
|
||||
libc.src.__support.threads.mutex
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
mtx_unlock
|
||||
SRCS
|
||||
@@ -193,6 +204,13 @@ add_entrypoint_object(
|
||||
.${LIBC_TARGET_OS}.cnd_wait
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
cnd_timedwait
|
||||
ALIAS
|
||||
DEPENDS
|
||||
.${LIBC_TARGET_OS}.cnd_timedwait
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
cnd_signal
|
||||
ALIAS
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
//===-- Internal header for cnd_timedwait -----------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC_THREADS_CND_TIMEDWAIT_H
|
||||
#define LLVM_LIBC_SRC_THREADS_CND_TIMEDWAIT_H
|
||||
|
||||
#include "src/__support/macros/config.h"
|
||||
#include <threads.h>
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
int cnd_timedwait(cnd_t *__restrict cond, mtx_t *__restrict mutex,
|
||||
const struct timespec *__restrict time_point);
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC_THREADS_CND_TIMEDWAIT_H
|
||||
@@ -48,6 +48,20 @@ add_entrypoint_object(
|
||||
libc.src.__support.threads.CndVar
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
cnd_timedwait
|
||||
SRCS
|
||||
cnd_timedwait.cpp
|
||||
HDRS
|
||||
../cnd_timedwait.h
|
||||
DEPENDS
|
||||
libc.include.threads
|
||||
libc.src.__support.threads.mutex
|
||||
libc.src.__support.threads.CndVar
|
||||
libc.src.__support.time.abs_timeout
|
||||
libc.src.__support.macros.null_check
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
cnd_signal
|
||||
SRCS
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//===-- Linux implementation of the cnd_timedwait function ----------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/threads/cnd_timedwait.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/macros/null_check.h"
|
||||
#include "src/__support/threads/CndVar.h"
|
||||
#include "src/__support/threads/mutex.h"
|
||||
#include "src/__support/time/abs_timeout.h"
|
||||
|
||||
#include <threads.h>
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
static_assert(sizeof(CndVar) == sizeof(cnd_t));
|
||||
static_assert(sizeof(Mutex) == sizeof(mtx_t) &&
|
||||
alignof(Mutex) == alignof(mtx_t));
|
||||
|
||||
LLVM_LIBC_FUNCTION(int, cnd_timedwait,
|
||||
(cnd_t *__restrict cond, mtx_t *__restrict mtx,
|
||||
const struct timespec *__restrict time_point)) {
|
||||
LIBC_CRASH_ON_NULLPTR(time_point);
|
||||
CndVar *cndvar = reinterpret_cast<CndVar *>(cond);
|
||||
Mutex *mutex = reinterpret_cast<Mutex *>(mtx);
|
||||
|
||||
// time_point is TIME_UTC-based, so we assume realtime clock here.
|
||||
auto timeout =
|
||||
internal::AbsTimeout::from_timespec(*time_point, /*realtime=*/true);
|
||||
|
||||
if (!timeout.has_value()) {
|
||||
switch (timeout.error()) {
|
||||
case internal::AbsTimeout::Error::BeforeEpoch:
|
||||
return thrd_timedout;
|
||||
case internal::AbsTimeout::Error::Invalid:
|
||||
return thrd_error;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
switch (cndvar->wait(mutex, timeout.value())) {
|
||||
case CndVarResult::Success:
|
||||
return thrd_success;
|
||||
case CndVarResult::Timeout:
|
||||
return thrd_timedout;
|
||||
case CndVarResult::MutexError:
|
||||
return thrd_error;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
@@ -0,0 +1,27 @@
|
||||
//===-- Linux implementation of the mtx_trylock function ----------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/threads/mtx_trylock.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/threads/mutex.h"
|
||||
|
||||
#include <threads.h> // For mtx_t definition.
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
// The implementation currently handles only plain mutexes.
|
||||
LLVM_LIBC_FUNCTION(int, mtx_trylock, (mtx_t * mutex)) {
|
||||
auto *m = reinterpret_cast<Mutex *>(mutex);
|
||||
auto err = m->try_lock();
|
||||
if (err == MutexError::BUSY)
|
||||
return thrd_busy;
|
||||
return err == MutexError::NONE ? thrd_success : thrd_error;
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
@@ -0,0 +1,21 @@
|
||||
//===-- Internal header for mtx_trylock -------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC_THREADS_MTX_TRYLOCK_H
|
||||
#define LLVM_LIBC_SRC_THREADS_MTX_TRYLOCK_H
|
||||
|
||||
#include "src/__support/macros/config.h"
|
||||
#include <threads.h>
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
int mtx_trylock(mtx_t *mutex);
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC_THREADS_MTX_TRYLOCK_H
|
||||
@@ -12,6 +12,7 @@ add_integration_test(
|
||||
libc.src.threads.mtx_destroy
|
||||
libc.src.threads.mtx_init
|
||||
libc.src.threads.mtx_lock
|
||||
libc.src.threads.mtx_trylock
|
||||
libc.src.threads.mtx_unlock
|
||||
libc.src.threads.thrd_create
|
||||
libc.src.threads.thrd_join
|
||||
@@ -104,12 +105,14 @@ add_integration_test(
|
||||
cnd_test.cpp
|
||||
DEPENDS
|
||||
libc.include.threads
|
||||
libc.include.time
|
||||
libc.src.__support.CPP.atomic
|
||||
libc.src.threads.cnd_init
|
||||
libc.src.threads.cnd_broadcast
|
||||
libc.src.threads.cnd_signal
|
||||
libc.src.threads.cnd_destroy
|
||||
libc.src.threads.cnd_wait
|
||||
libc.src.threads.cnd_timedwait
|
||||
libc.src.threads.mtx_destroy
|
||||
libc.src.threads.mtx_init
|
||||
libc.src.threads.mtx_lock
|
||||
@@ -117,4 +120,5 @@ add_integration_test(
|
||||
libc.src.threads.thrd_create
|
||||
libc.src.threads.thrd_join
|
||||
libc.src.threads.linux.threads_utils
|
||||
libc.src.time.timespec_get
|
||||
)
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "src/threads/cnd_destroy.h"
|
||||
#include "src/threads/cnd_init.h"
|
||||
#include "src/threads/cnd_signal.h"
|
||||
#include "src/threads/cnd_timedwait.h"
|
||||
#include "src/threads/cnd_wait.h"
|
||||
#include "src/threads/mtx_destroy.h"
|
||||
#include "src/threads/mtx_init.h"
|
||||
@@ -18,10 +19,20 @@
|
||||
#include "src/threads/mtx_unlock.h"
|
||||
#include "src/threads/thrd_create.h"
|
||||
#include "src/threads/thrd_join.h"
|
||||
#include "src/time/timespec_get.h"
|
||||
|
||||
#include "test/IntegrationTest/test.h"
|
||||
|
||||
#include <threads.h>
|
||||
#include <time.h> // for TIME_UTC
|
||||
|
||||
static void add_ns(struct timespec &ts, long ns) {
|
||||
ts.tv_nsec += ns;
|
||||
if (ts.tv_nsec >= 1'000'000'000) {
|
||||
++ts.tv_sec;
|
||||
ts.tv_nsec -= 1'000'000'000;
|
||||
}
|
||||
}
|
||||
|
||||
namespace wait_notify_broadcast_test {
|
||||
|
||||
@@ -100,6 +111,11 @@ namespace single_waiter_test {
|
||||
mtx_t waiter_mtx, main_thread_mtx;
|
||||
cnd_t waiter_cnd, main_thread_cnd;
|
||||
|
||||
enum class WaitMode {
|
||||
Default,
|
||||
Timed,
|
||||
};
|
||||
|
||||
int waiter_thread_func([[maybe_unused]] void *unused) {
|
||||
LIBC_NAMESPACE::mtx_lock(&waiter_mtx);
|
||||
|
||||
@@ -113,7 +129,7 @@ int waiter_thread_func([[maybe_unused]] void *unused) {
|
||||
return 0x600D;
|
||||
}
|
||||
|
||||
void single_waiter_test() {
|
||||
void single_waiter_test(WaitMode wait_mode) {
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&waiter_mtx, mtx_plain),
|
||||
int(thrd_success));
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&main_thread_mtx, mtx_plain),
|
||||
@@ -126,8 +142,18 @@ void single_waiter_test() {
|
||||
thrd_t waiter_thread;
|
||||
LIBC_NAMESPACE::thrd_create(&waiter_thread, waiter_thread_func, nullptr);
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::cnd_wait(&main_thread_cnd, &main_thread_mtx),
|
||||
int(thrd_success));
|
||||
if (wait_mode == WaitMode::Default) {
|
||||
ASSERT_EQ(LIBC_NAMESPACE::cnd_wait(&main_thread_cnd, &main_thread_mtx),
|
||||
int(thrd_success));
|
||||
} else {
|
||||
ASSERT_EQ(wait_mode, WaitMode::Timed);
|
||||
struct timespec ts;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::timespec_get(&ts, TIME_UTC), TIME_UTC);
|
||||
add_ns(ts, 50'000);
|
||||
int result =
|
||||
LIBC_NAMESPACE::cnd_timedwait(&main_thread_cnd, &main_thread_mtx, &ts);
|
||||
ASSERT_TRUE(result == int(thrd_success) || result == int(thrd_timedout));
|
||||
}
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&main_thread_mtx), int(thrd_success));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&waiter_mtx), int(thrd_success));
|
||||
@@ -146,8 +172,53 @@ void single_waiter_test() {
|
||||
|
||||
} // namespace single_waiter_test
|
||||
|
||||
namespace timed_wait_test {
|
||||
|
||||
void timeout_test() {
|
||||
cnd_t cnd;
|
||||
mtx_t mtx;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::cnd_init(&cnd), int(thrd_success));
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&mtx, mtx_plain), int(thrd_success));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&mtx), int(thrd_success));
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::cnd_timedwait(&cnd, &mtx, &ts), int(thrd_timedout));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&mtx), int(thrd_success));
|
||||
|
||||
LIBC_NAMESPACE::cnd_destroy(&cnd);
|
||||
LIBC_NAMESPACE::mtx_destroy(&mtx);
|
||||
}
|
||||
|
||||
void future_timeout_test() {
|
||||
cnd_t cnd;
|
||||
mtx_t mtx;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::cnd_init(&cnd), int(thrd_success));
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&mtx, mtx_plain), int(thrd_success));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&mtx), int(thrd_success));
|
||||
|
||||
struct timespec ts;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::timespec_get(&ts, TIME_UTC), TIME_UTC);
|
||||
add_ns(ts, 50'000);
|
||||
ASSERT_EQ(LIBC_NAMESPACE::cnd_timedwait(&cnd, &mtx, &ts), int(thrd_timedout));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&mtx), int(thrd_success));
|
||||
|
||||
LIBC_NAMESPACE::cnd_destroy(&cnd);
|
||||
LIBC_NAMESPACE::mtx_destroy(&mtx);
|
||||
}
|
||||
|
||||
} // namespace timed_wait_test
|
||||
|
||||
TEST_MAIN() {
|
||||
wait_notify_broadcast_test::wait_notify_broadcast_test();
|
||||
single_waiter_test::single_waiter_test();
|
||||
single_waiter_test::single_waiter_test(single_waiter_test::WaitMode::Default);
|
||||
single_waiter_test::single_waiter_test(single_waiter_test::WaitMode::Timed);
|
||||
timed_wait_test::timeout_test();
|
||||
timed_wait_test::future_timeout_test();
|
||||
return 0;
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "src/threads/mtx_destroy.h"
|
||||
#include "src/threads/mtx_init.h"
|
||||
#include "src/threads/mtx_lock.h"
|
||||
#include "src/threads/mtx_trylock.h"
|
||||
#include "src/threads/mtx_unlock.h"
|
||||
#include "src/threads/thrd_create.h"
|
||||
#include "src/threads/thrd_join.h"
|
||||
@@ -230,10 +231,36 @@ void multiple_waiters() {
|
||||
LIBC_NAMESPACE::mtx_destroy(&counter_lock);
|
||||
}
|
||||
|
||||
void trylock_test() {
|
||||
mtx_t plain_mutex;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&plain_mutex, mtx_plain),
|
||||
int(thrd_success));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_trylock(&plain_mutex), int(thrd_success));
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_trylock(&plain_mutex), int(thrd_busy));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&plain_mutex), int(thrd_success));
|
||||
|
||||
LIBC_NAMESPACE::mtx_destroy(&plain_mutex);
|
||||
|
||||
mtx_t recursive_mutex;
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&recursive_mutex, mtx_recursive),
|
||||
int(thrd_success));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_trylock(&recursive_mutex), int(thrd_success));
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_trylock(&recursive_mutex), int(thrd_success));
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&recursive_mutex), int(thrd_success));
|
||||
ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&recursive_mutex), int(thrd_success));
|
||||
|
||||
LIBC_NAMESPACE::mtx_destroy(&recursive_mutex);
|
||||
}
|
||||
|
||||
TEST_MAIN() {
|
||||
relay_counter();
|
||||
wait_and_step();
|
||||
recursive_mutex_test();
|
||||
multiple_waiters();
|
||||
trylock_test();
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user