aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Dionne <ldionne.2@gmail.com>2024-07-31 08:27:01 -0500
committerGitHub <noreply@github.com>2024-07-31 09:27:01 -0400
commit29ef92b9051bca3a35be0cbc5e03d787941f945e (patch)
treee3e230f2c2a77dfb3377f39510ed0148628f1359
parentd8b985c9490664f7ec923e59cf6c603d998179ae (diff)
downloadllvm-29ef92b9051bca3a35be0cbc5e03d787941f945e.zip
llvm-29ef92b9051bca3a35be0cbc5e03d787941f945e.tar.gz
llvm-29ef92b9051bca3a35be0cbc5e03d787941f945e.tar.bz2
[libc++] Refactor tests for shared_mutex and shared_timed_mutex (#100783)
This makes the tests less flaky and also makes a few other refactorings like using traits instead of .compile.fail.cpp tests.
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp (renamed from libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.verify.cpp)12
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp (renamed from libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.verify.cpp)11
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.default.pass.cpp (renamed from libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp)11
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp110
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp172
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp66
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp93
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp (renamed from libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.fail.cpp)11
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp (renamed from libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/copy.compile.fail.cpp)10
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp (renamed from libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/default.pass.cpp)2
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock.pass.cpp122
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock_shared.pass.cpp194
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock.pass.cpp66
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_for.pass.cpp125
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared.pass.cpp92
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_for.pass.cpp159
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_until.pass.cpp142
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_until.pass.cpp126
18 files changed, 900 insertions, 624 deletions
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.verify.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp
index 34164aa..0d90bff 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.verify.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
@@ -16,12 +16,6 @@
// shared_mutex& operator=(const shared_mutex&) = delete;
#include <shared_mutex>
+#include <type_traits>
-int main(int, char**)
-{
- std::shared_mutex m0;
- std::shared_mutex m1;
- m1 = m0; // expected-error {{overload resolution selected deleted operator '='}}
-
- return 0;
-}
+static_assert(!std::is_copy_assignable<std::shared_mutex>::value, "");
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.verify.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp
index 9b43198..f9e1935 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.verify.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
@@ -16,11 +16,6 @@
// shared_mutex(const shared_mutex&) = delete;
#include <shared_mutex>
+#include <type_traits>
-int main(int, char**)
-{
- std::shared_mutex m0;
- std::shared_mutex m1(m0); // expected-error {{call to deleted constructor of 'std::shared_mutex'}}
-
- return 0;
-}
+static_assert(!std::is_copy_constructible<std::shared_mutex>::value, "");
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.default.pass.cpp
index 5504645..c941f3a 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.default.pass.cpp
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
@@ -19,10 +19,9 @@
#include "test_macros.h"
-int main(int, char**)
-{
- std::shared_mutex m;
- (void)m;
+int main(int, char**) {
+ std::shared_mutex m;
+ (void)m;
- return 0;
+ return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp
index 122f2b0..724fb07 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp
@@ -5,63 +5,105 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_mutex;
// void lock();
+#include <shared_mutex>
+#include <atomic>
#include <cassert>
#include <chrono>
-#include <cstdlib>
-#include <shared_mutex>
#include <thread>
+#include <vector>
#include "make_test_thread.h"
#include "test_macros.h"
-std::shared_mutex m;
+int main(int, char**) {
+ // Exclusive-lock a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_mutex m;
+ m.lock();
+ m.unlock();
+ }
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
+ // Exclusive-lock a mutex that is already locked exclusively. This should block until it is unlocked.
+ {
+ std::atomic<bool> ready(false);
+ std::shared_mutex m;
+ m.lock();
+ std::atomic<bool> is_locked_from_main(true);
-ms WaitTime = ms(250);
+ std::thread t = support::make_test_thread([&] {
+ ready = true;
+ m.lock();
+ assert(!is_locked_from_main);
+ m.unlock();
+ });
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(50);
-#else
-ms Tolerance = ms(50 * 5);
-#endif
+ while (!ready)
+ /* spin */;
-void f()
-{
- time_point t0 = Clock::now();
- m.lock();
- time_point t1 = Clock::now();
+ // We would rather signal this after we unlock, but that would create a race condition.
+ // We instead signal it before we unlock, which means that it's technically possible for the thread
+ // to take the lock while we're still holding it and for the test to still pass.
+ is_locked_from_main = false;
m.unlock();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within tolerance
-}
-int main(int, char**)
-{
- m.lock();
- std::thread t = support::make_test_thread(f);
- std::this_thread::sleep_for(WaitTime);
- m.unlock();
t.join();
+ }
+
+ // Exclusive-lock a mutex that is already share-locked. This should block until it is unlocked.
+ {
+ std::atomic<bool> ready(false);
+ std::shared_mutex m;
+ m.lock_shared();
+ std::atomic<bool> is_locked_from_main(true);
+
+ std::thread t = support::make_test_thread([&] {
+ ready = true;
+ m.lock();
+ assert(!is_locked_from_main);
+ m.unlock();
+ });
+
+ while (!ready)
+ /* spin */;
+
+ // We would rather signal this after we unlock, but that would create a race condition.
+ // We instead signal it before we unlock, which means that it's technically possible for
+ // the thread to take the lock while we're still holding it and for the test to still pass.
+ is_locked_from_main = false;
+ m.unlock_shared();
+
+ t.join();
+ }
+
+ // Make sure that at most one thread can acquire the mutex concurrently.
+ {
+ std::atomic<int> counter = 0;
+ std::shared_mutex mutex;
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 10; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ mutex.lock();
+ counter++;
+ assert(counter == 1);
+ counter--;
+ mutex.unlock();
+ }));
+ }
+
+ for (auto& t : threads)
+ t.join();
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp
index 9df0d57..e6640f7 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp
@@ -5,87 +5,139 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_mutex;
// void lock_shared();
-#include <cassert>
-#include <chrono>
-#include <cstdlib>
#include <shared_mutex>
+#include <algorithm>
+#include <atomic>
+#include <cassert>
#include <thread>
#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_mutex m;
-
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-ms WaitTime = ms(250);
-
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(50);
-#else
-ms Tolerance = ms(50 * 5);
-#endif
-
-void f()
-{
- time_point t0 = Clock::now();
- m.lock_shared();
- time_point t1 = Clock::now();
- m.unlock_shared();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within tolerance
-}
-void g()
-{
- time_point t0 = Clock::now();
- m.lock_shared();
- time_point t1 = Clock::now();
- m.unlock_shared();
- ns d = t1 - t0;
- assert(d < Tolerance); // within tolerance
-}
+int main(int, char**) {
+ // Lock-shared a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_mutex m;
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ m.lock_shared();
+ m.unlock_shared();
+ }));
+ }
+ for (auto& t : threads)
+ t.join();
+ }
-int main(int, char**)
-{
+ // Lock-shared a mutex that is already exclusively locked. This should block until it is unlocked.
+ {
+ std::atomic<int> ready(0);
+ std::shared_mutex m;
m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < 5; ++i)
- v.push_back(support::make_test_thread(f));
- std::this_thread::sleep_for(WaitTime);
+ std::atomic<bool> is_locked_from_main(true);
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ ++ready;
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ m.lock_shared();
+ assert(!is_locked_from_main);
+ m.unlock_shared();
+ }));
+ }
+
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ // We would rather signal this after we unlock, but that would create a race condition.
+ // We instead signal it before we unlock, which means that it's technically possible for
+ // the thread to take the lock while we're still holding it and for the test to still pass.
+ is_locked_from_main = false;
m.unlock();
- for (auto& t : v)
- t.join();
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Lock-shared a mutex that is already lock-shared. This should succeed.
+ {
+ std::atomic<int> ready(0);
+ std::shared_mutex m;
m.lock_shared();
- for (auto& t : v)
- t = support::make_test_thread(g);
- std::thread q = support::make_test_thread(f);
- std::this_thread::sleep_for(WaitTime);
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ ++ready;
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ m.lock_shared();
+ m.unlock_shared();
+ }));
+ }
+
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
m.unlock_shared();
- for (auto& t : v)
- t.join();
- q.join();
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Create several threads that all acquire-shared the same mutex and make sure that each
+ // thread successfully acquires-shared the mutex.
+ //
+ // We record how many other threads were holding the mutex when it was acquired, which allows
+ // us to know whether the test was somewhat effective at causing multiple threads to lock at
+ // the same time.
+ {
+ std::shared_mutex mutex;
+ std::vector<std::thread> threads;
+ constexpr int n_threads = 5;
+ std::atomic<int> holders = 0;
+ int concurrent_holders[n_threads] = {};
+ std::atomic<bool> ready = false;
+
+ for (int i = 0; i != n_threads; ++i) {
+ threads.push_back(support::make_test_thread([&, i] {
+ while (!ready) {
+ // spin
+ }
+
+ mutex.lock_shared();
+ ++holders;
+ concurrent_holders[i] = holders;
+
+ mutex.unlock_shared();
+ --holders;
+ }));
+ }
+
+ ready = true; // let the threads actually start shared-acquiring the mutex
+ for (auto& t : threads)
+ t.join();
+
+ // We can't guarantee that we'll ever have more than 1 concurrent holder so that's what
+ // we assert, however in principle we should often trigger more than 1 concurrent holder.
+ int max_concurrent_holders = *std::max_element(std::begin(concurrent_holders), std::end(concurrent_holders));
+ assert(max_concurrent_holders >= 1);
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp
index f39b1ee..11d396d 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp
@@ -5,56 +5,60 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_mutex;
// bool try_lock();
+#include <shared_mutex>
+#include <atomic>
#include <cassert>
#include <chrono>
-#include <cstdlib>
-#include <shared_mutex>
#include <thread>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_mutex m;
-
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-void f()
-{
- time_point t0 = Clock::now();
- assert(!m.try_lock());
- assert(!m.try_lock());
- assert(!m.try_lock());
- while(!m.try_lock())
- ;
- time_point t1 = Clock::now();
+
+int main(int, char**) {
+ // Try to exclusive-lock a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_mutex m;
+ bool succeeded = m.try_lock();
+ assert(succeeded);
m.unlock();
- ns d = t1 - t0 - ms(250);
- assert(d < ms(200)); // within 200ms
-}
+ }
-int main(int, char**)
-{
+ // Try to exclusive-lock a mutex that is already locked exclusively. This should fail.
+ {
+ std::shared_mutex m;
m.lock();
- std::thread t = support::make_test_thread(f);
- std::this_thread::sleep_for(ms(250));
+
+ std::thread t = support::make_test_thread([&] {
+ bool succeeded = m.try_lock();
+ assert(!succeeded);
+ });
+ t.join();
+
m.unlock();
+ }
+
+ // Try to exclusive-lock a mutex that is already share-locked. This should fail.
+ {
+ std::shared_mutex m;
+ m.lock_shared();
+
+ std::thread t = support::make_test_thread([&] {
+ bool succeeded = m.try_lock();
+ assert(!succeeded);
+ });
t.join();
+ m.unlock_shared();
+ }
+
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp
index c091b06..61069be 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp
@@ -5,71 +5,76 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_mutex;
// bool try_lock_shared();
-#include <cassert>
-#include <chrono>
-#include <cstdlib>
#include <shared_mutex>
+#include <cassert>
#include <thread>
#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-std::shared_mutex m;
+int main(int, char**) {
+ // Try to lock-shared a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_mutex m;
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared();
+ assert(succeeded);
+ m.unlock_shared();
+ }));
+ }
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
+ for (auto& t : threads)
+ t.join();
+ }
+ // Try to lock-shared a mutex that is already exclusively locked. This should fail.
+ {
+ std::shared_mutex m;
+ m.lock();
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(200);
-#else
-ms Tolerance = ms(200 * 5);
-#endif
-
-void f()
-{
- time_point t0 = Clock::now();
- assert(!m.try_lock_shared());
- assert(!m.try_lock_shared());
- assert(!m.try_lock_shared());
- while(!m.try_lock_shared())
- std::this_thread::yield();
- time_point t1 = Clock::now();
- m.unlock_shared();
- ns d = t1 - t0 - ms(250);
- assert(d < Tolerance); // within tolerance
-}
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared();
+ assert(!succeeded);
+ }));
+ }
+ for (auto& t : threads)
+ t.join();
-int main(int, char**)
-{
- m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < 5; ++i)
- v.push_back(support::make_test_thread(f));
- std::this_thread::sleep_for(ms(250));
m.unlock();
- for (auto& t : v)
- t.join();
+ }
+
+ // Try to lock-shared a mutex that is already lock-shared. This should succeed.
+ {
+ std::shared_mutex m;
+ m.lock_shared();
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared();
+ assert(succeeded);
+ m.unlock_shared();
+ }));
+ }
+ m.unlock_shared();
+
+ for (auto& t : threads)
+ t.join();
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.fail.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp
index c2cd893..9cbb0b1 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.fail.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
// <shared_mutex>
@@ -15,12 +16,6 @@
// shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
#include <shared_mutex>
+#include <type_traits>
-int main(int, char**)
-{
- std::shared_timed_mutex m0;
- std::shared_timed_mutex m1;
- m1 = m0;
-
- return 0;
-}
+static_assert(!std::is_copy_assignable<std::shared_timed_mutex>::value, "");
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/copy.compile.fail.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp
index 9b0a661..12b01a5 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/copy.compile.fail.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
// <shared_mutex>
@@ -15,11 +16,6 @@
// shared_timed_mutex(const shared_timed_mutex&) = delete;
#include <shared_mutex>
+#include <type_traits>
-int main(int, char**)
-{
- std::shared_timed_mutex m0;
- std::shared_timed_mutex m1(m0);
-
- return 0;
-}
+static_assert(!std::is_copy_constructible<std::shared_timed_mutex>::value, "");
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/default.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp
index 7a8d096..eadc59e 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/default.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock.pass.cpp
index acabbab..f78b13d 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock.pass.cpp
@@ -5,62 +5,104 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 3
-
// <shared_mutex>
// class shared_timed_mutex;
// void lock();
+#include <shared_mutex>
#include <atomic>
#include <cassert>
#include <chrono>
-#include <cstdlib>
-#include <shared_mutex>
#include <thread>
+#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_timed_mutex m;
-
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-std::atomic<bool> ready(false);
-time_point start;
-
-ms WaitTime = ms(250);
-
-void f()
-{
- ready.store(true);
- m.lock();
- time_point t0 = start;
- time_point t1 = Clock::now();
- m.unlock();
- assert(t0.time_since_epoch() > ms(0));
- assert(t1 - t0 >= WaitTime);
-}
-int main(int, char**)
-{
- m.lock();
- std::thread t = support::make_test_thread(f);
- while (!ready)
- std::this_thread::yield();
- start = Clock::now();
- std::this_thread::sleep_for(WaitTime);
- m.unlock();
- t.join();
+int main(int, char**) {
+ // Exclusive-lock a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_timed_mutex m;
+ m.lock();
+ m.unlock();
+ }
+
+ // Exclusive-lock a mutex that is already locked exclusively. This should block until it is unlocked.
+ {
+ std::atomic<bool> ready(false);
+ std::shared_timed_mutex m;
+ m.lock();
+ std::atomic<bool> is_locked_from_main(true);
+
+ std::thread t = support::make_test_thread([&] {
+ ready = true;
+ m.lock();
+ assert(!is_locked_from_main);
+ m.unlock();
+ });
+
+ while (!ready)
+ /* spin */;
+
+ // We would rather signal this after we unlock, but that would create a race condition.
+ // We instead signal it before we unlock, which means that it's technically possible for the thread
+ // to take the lock while we're still holding it and for the test to still pass.
+ is_locked_from_main = false;
+ m.unlock();
+
+ t.join();
+ }
+
+ // Exclusive-lock a mutex that is already share-locked. This should block until it is unlocked.
+ {
+ std::atomic<bool> ready(false);
+ std::shared_timed_mutex m;
+ m.lock_shared();
+ std::atomic<bool> is_locked_from_main(true);
+
+ std::thread t = support::make_test_thread([&] {
+ ready = true;
+ m.lock();
+ assert(!is_locked_from_main);
+ m.unlock();
+ });
+
+ while (!ready)
+ /* spin */;
+
+ // We would rather signal this after we unlock, but that would create a race condition.
+ // We instead signal it before we unlock, which means that it's technically possible for
+ // the thread to take the lock while we're still holding it and for the test to still pass.
+ is_locked_from_main = false;
+ m.unlock_shared();
+
+ t.join();
+ }
+
+ // Make sure that at most one thread can acquire the mutex concurrently.
+ {
+ std::atomic<int> counter(0);
+ std::shared_timed_mutex mutex;
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 10; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ mutex.lock();
+ counter++;
+ assert(counter == 1);
+ counter--;
+ mutex.unlock();
+ }));
+ }
+
+ for (auto& t : threads)
+ t.join();
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock_shared.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock_shared.pass.cpp
index 36f5dba..d9a3db1 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock_shared.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/lock_shared.pass.cpp
@@ -5,100 +5,138 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 3
-
// <shared_mutex>
// class shared_timed_mutex;
// void lock_shared();
-
+#include <shared_mutex>
+#include <algorithm>
#include <atomic>
#include <cassert>
-#include <chrono>
-#include <cstdlib>
-#include <shared_mutex>
#include <thread>
#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_timed_mutex m;
-
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-std::atomic<unsigned> countDown;
-time_point readerStart; // Protected by the above mutex 'm'
-time_point writerStart; // Protected by the above mutex 'm'
-
-ms WaitTime = ms(250);
-
-void readerMustWait() {
- --countDown;
- m.lock_shared();
- time_point t1 = Clock::now();
- time_point t0 = readerStart;
- m.unlock_shared();
- assert(t0.time_since_epoch() > ms(0));
- assert(t1 - t0 >= WaitTime);
-}
-
-void reader() {
- --countDown;
- m.lock_shared();
- m.unlock_shared();
-}
-
-void writerMustWait() {
- --countDown;
- m.lock();
- time_point t1 = Clock::now();
- time_point t0 = writerStart;
- m.unlock();
- assert(t0.time_since_epoch() > ms(0));
- assert(t1 - t0 >= WaitTime);
-}
-int main(int, char**)
-{
- int threads = 5;
-
- countDown.store(threads);
- m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < threads; ++i)
- v.push_back(support::make_test_thread(readerMustWait));
- while (countDown > 0)
- std::this_thread::yield();
- readerStart = Clock::now();
- std::this_thread::sleep_for(WaitTime);
- m.unlock();
- for (auto& t : v)
- t.join();
-
- countDown.store(threads + 1);
- m.lock_shared();
- for (auto& t : v)
- t = support::make_test_thread(reader);
- std::thread q = support::make_test_thread(writerMustWait);
- while (countDown > 0)
- std::this_thread::yield();
- writerStart = Clock::now();
- std::this_thread::sleep_for(WaitTime);
- m.unlock_shared();
- for (auto& t : v)
- t.join();
- q.join();
+int main(int, char**) {
+ // Lock-shared a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_timed_mutex m;
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ m.lock_shared();
+ m.unlock_shared();
+ }));
+ }
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Lock-shared a mutex that is already exclusively locked. This should block until it is unlocked.
+ {
+ std::atomic<int> ready(0);
+ std::shared_timed_mutex m;
+ m.lock();
+ std::atomic<bool> is_locked_from_main(true);
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ ++ready;
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ m.lock_shared();
+ assert(!is_locked_from_main);
+ m.unlock_shared();
+ }));
+ }
+
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ // We would rather signal this after we unlock, but that would create a race condition.
+ // We instead signal it before we unlock, which means that it's technically possible for
+ // the thread to take the lock while we're still holding it and for the test to still pass.
+ is_locked_from_main = false;
+ m.unlock();
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Lock-shared a mutex that is already lock-shared. This should succeed.
+ {
+ std::atomic<int> ready(0);
+ std::shared_timed_mutex m;
+ m.lock_shared();
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ ++ready;
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ m.lock_shared();
+ m.unlock_shared();
+ }));
+ }
+
+ while (ready < 5)
+ /* wait until all threads have been created */;
+
+ m.unlock_shared();
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Create several threads that all acquire-shared the same mutex and make sure that each
+ // thread successfully acquires-shared the mutex.
+ //
+ // We record how many other threads were holding the mutex when it was acquired, which allows
+ // us to know whether the test was somewhat effective at causing multiple threads to lock at
+ // the same time.
+ {
+ std::shared_timed_mutex mutex;
+ std::vector<std::thread> threads;
+ constexpr int n_threads = 5;
+ std::atomic<int> holders(0);
+ int concurrent_holders[n_threads] = {};
+ std::atomic<bool> ready(false);
+
+ for (int i = 0; i != n_threads; ++i) {
+ threads.push_back(support::make_test_thread([&, i] {
+ while (!ready)
+ /* spin */;
+
+ mutex.lock_shared();
+ ++holders;
+ concurrent_holders[i] = holders;
+
+ mutex.unlock_shared();
+ --holders;
+ }));
+ }
+
+ ready = true; // let the threads actually start shared-acquiring the mutex
+ for (auto& t : threads)
+ t.join();
+
+ // We can't guarantee that we'll ever have more than 1 concurrent holder so that's what
+ // we assert, however in principle we should often trigger more than 1 concurrent holder.
+ int max_concurrent_holders = *std::max_element(std::begin(concurrent_holders), std::end(concurrent_holders));
+ assert(max_concurrent_holders >= 1);
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock.pass.cpp
index cc7091f..9ed8b5b 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock.pass.cpp
@@ -5,56 +5,60 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_timed_mutex;
// bool try_lock();
+#include <shared_mutex>
+#include <atomic>
#include <cassert>
#include <chrono>
-#include <cstdlib>
-#include <shared_mutex>
#include <thread>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_timed_mutex m;
-
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-void f()
-{
- time_point t0 = Clock::now();
- assert(!m.try_lock());
- assert(!m.try_lock());
- assert(!m.try_lock());
- while(!m.try_lock())
- ;
- time_point t1 = Clock::now();
+
+int main(int, char**) {
+ // Try to exclusive-lock a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_timed_mutex m;
+ bool succeeded = m.try_lock();
+ assert(succeeded);
m.unlock();
- ns d = t1 - t0 - ms(250);
- assert(d < ms(200)); // within 200ms
-}
+ }
-int main(int, char**)
-{
+ // Try to exclusive-lock a mutex that is already locked exclusively. This should fail.
+ {
+ std::shared_timed_mutex m;
m.lock();
- std::thread t = support::make_test_thread(f);
- std::this_thread::sleep_for(ms(250));
+
+ std::thread t = support::make_test_thread([&] {
+ bool succeeded = m.try_lock();
+ assert(!succeeded);
+ });
+ t.join();
+
m.unlock();
+ }
+
+ // Try to exclusive-lock a mutex that is already share-locked. This should fail.
+ {
+ std::shared_timed_mutex m;
+ m.lock_shared();
+
+ std::thread t = support::make_test_thread([&] {
+ bool succeeded = m.try_lock();
+ assert(!succeeded);
+ });
t.join();
+ m.unlock_shared();
+ }
+
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_for.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_for.pass.cpp
index 30fc3c5..0ae9a48 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_for.pass.cpp
@@ -5,10 +5,9 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 2
// <shared_mutex>
@@ -18,69 +17,89 @@
// bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
#include <shared_mutex>
-#include <thread>
-#include <cstdlib>
+#include <atomic>
#include <cassert>
#include <chrono>
+#include <thread>
#include "make_test_thread.h"
-#include "test_macros.h"
-std::shared_timed_mutex m;
+template <class Function>
+std::chrono::microseconds measure(Function f) {
+ std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
+ f();
+ std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
+ return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+}
-typedef std::chrono::steady_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
+int main(int, char**) {
+ // Try to lock a mutex that is not locked yet. This should succeed immediately.
+ {
+ std::shared_timed_mutex m;
+ bool succeeded = m.try_lock_for(std::chrono::milliseconds(1));
+ assert(succeeded);
+ m.unlock();
+ }
+ // Try to lock an already-locked mutex for a long enough amount of time and succeed.
+ // This is technically flaky, but we use such long durations that it should pass even
+ // in slow or contended environments.
+ {
+ std::chrono::milliseconds const wait_time(500);
+ std::chrono::milliseconds const tolerance = wait_time * 3;
+ std::atomic<bool> ready(false);
-ms WaitTime = ms(250);
+ std::shared_timed_mutex m;
+ m.lock();
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(50);
-#else
-ms Tolerance = ms(50 * 5);
-#endif
+ std::thread t = support::make_test_thread([&] {
+ auto elapsed = measure([&] {
+ ready = true;
+ bool succeeded = m.try_lock_for(wait_time);
+ assert(succeeded);
+ m.unlock();
+ });
-void f1()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_for(WaitTime + Tolerance) == true);
- time_point t1 = Clock::now();
- m.unlock();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within tolerance
-}
+ // Ensure we didn't wait significantly longer than our timeout. This is technically
+ // flaky and non-conforming because an implementation is free to block for arbitrarily
+ // long, but any decent quality implementation should pass this test.
+ assert(elapsed - wait_time < tolerance);
+ });
-void f2()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_for(WaitTime) == false);
- time_point t1 = Clock::now();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within tolerance
-}
+ // Wait for the thread to be ready to take the lock before we unlock it from here, otherwise
+ // there's a high chance that we're not testing the "locking an already locked" mutex use case.
+ // There is still technically a race condition here.
+ while (!ready)
+ /* spin */;
+ std::this_thread::sleep_for(wait_time / 5);
-int main(int, char**)
-{
- {
- m.lock();
- std::thread t = support::make_test_thread(f1);
- std::this_thread::sleep_for(WaitTime);
- m.unlock();
- t.join();
- }
- {
- m.lock();
- std::thread t = support::make_test_thread(f2);
- std::this_thread::sleep_for(WaitTime + Tolerance);
- m.unlock();
- t.join();
- }
+ m.unlock(); // this should allow the thread to lock 'm'
+ t.join();
+ }
+
+ // Try to lock an already-locked mutex for a short amount of time and fail.
+ // Again, this is technically flaky but we use such long durations that it should work.
+ {
+ std::chrono::milliseconds const wait_time(10);
+ std::chrono::milliseconds const tolerance(750); // in case the thread we spawned goes to sleep or something
+
+ std::shared_timed_mutex m;
+ m.lock();
+
+ std::thread t = support::make_test_thread([&] {
+ auto elapsed = measure([&] {
+ bool succeeded = m.try_lock_for(wait_time);
+ assert(!succeeded);
+ });
+
+ // Ensure we failed within some bounded time.
+ assert(elapsed - wait_time < tolerance);
+ });
+
+ t.join();
+
+ m.unlock();
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared.pass.cpp
index 8523df0..6430a32 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared.pass.cpp
@@ -5,70 +5,76 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_timed_mutex;
// bool try_lock_shared();
-#include <cassert>
-#include <chrono>
-#include <cstdlib>
#include <shared_mutex>
+#include <cassert>
#include <thread>
#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-std::shared_timed_mutex m;
+int main(int, char**) {
+ // Try to lock-shared a mutex that is not locked yet. This should succeed.
+ {
+ std::shared_timed_mutex m;
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared();
+ assert(succeeded);
+ m.unlock_shared();
+ }));
+ }
+
+ for (auto& t : threads)
+ t.join();
+ }
-typedef std::chrono::system_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
+ // Try to lock-shared a mutex that is already exclusively locked. This should fail.
+ {
+ std::shared_timed_mutex m;
+ m.lock();
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared();
+ assert(!succeeded);
+ }));
+ }
+ for (auto& t : threads)
+ t.join();
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(200);
-#else
-ms Tolerance = ms(200 * 5);
-#endif
+ m.unlock();
+ }
-void f()
-{
- time_point t0 = Clock::now();
- assert(!m.try_lock_shared());
- assert(!m.try_lock_shared());
- assert(!m.try_lock_shared());
- while(!m.try_lock_shared())
- std::this_thread::yield();
- time_point t1 = Clock::now();
+ // Try to lock-shared a mutex that is already lock-shared. This should succeed.
+ {
+ std::shared_timed_mutex m;
+ m.lock_shared();
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared();
+ assert(succeeded);
+ m.unlock_shared();
+ }));
+ }
m.unlock_shared();
- ns d = t1 - t0 - ms(250);
- assert(d < Tolerance); // within tolerance
-}
-int main(int, char**)
-{
- m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < 5; ++i)
- v.push_back(support::make_test_thread(f));
- std::this_thread::sleep_for(ms(250));
- m.unlock();
- for (auto& t : v)
- t.join();
+ for (auto& t : threads)
+ t.join();
+ }
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_for.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_for.pass.cpp
index c7d02a3..23a88ba 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_for.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_for.pass.cpp
@@ -5,12 +5,10 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 3
-
// <shared_mutex>
// class shared_timed_mutex;
@@ -19,75 +17,110 @@
// bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
#include <shared_mutex>
-#include <thread>
-#include <vector>
-#include <cstdlib>
+#include <atomic>
#include <cassert>
#include <chrono>
+#include <thread>
+#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_timed_mutex m;
-
-typedef std::chrono::steady_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-ms WaitTime = ms(250);
-
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(50);
-#else
-ms Tolerance = ms(50 * 5);
-#endif
-
-void f1()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_shared_for(WaitTime + Tolerance) == true);
- time_point t1 = Clock::now();
- m.unlock_shared();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within 50ms
-}
-void f2()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_shared_for(WaitTime) == false);
- time_point t1 = Clock::now();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within 50ms
+template <class Function>
+std::chrono::microseconds measure(Function f) {
+ std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
+ f();
+ std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
+ return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}
-int main(int, char**)
-{
- {
- m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < 5; ++i)
- v.push_back(support::make_test_thread(f1));
- std::this_thread::sleep_for(WaitTime);
- m.unlock();
- for (auto& t : v)
- t.join();
+int main(int, char**) {
+ // Try to lock-shared a mutex that is not locked yet. This should succeed immediately.
+ {
+ std::shared_timed_mutex m;
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared_for(std::chrono::milliseconds(1));
+ assert(succeeded);
+ m.unlock_shared();
+ }));
+ }
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Try to lock-shared an already-locked mutex for a long enough amount of time and succeed.
+ // This is technically flaky, but we use such long durations that it should pass even
+ // in slow or contended environments.
+ {
+ std::chrono::milliseconds const wait_time(500);
+ std::chrono::milliseconds const tolerance = wait_time * 3;
+ std::atomic<int> ready(0);
+
+ std::shared_timed_mutex m;
+ m.lock();
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ ++ready;
+ while (ready < 5)
+ /* spin until all threads are created */;
+
+ auto elapsed = measure([&] {
+ bool succeeded = m.try_lock_shared_for(wait_time);
+ assert(succeeded);
+ m.unlock_shared();
+ });
+
+ // Ensure we didn't wait significantly longer than our timeout. This is technically
+ // flaky and non-conforming because an implementation is free to block for arbitrarily
+ // long, but any decent quality implementation should pass this test.
+ assert(elapsed - wait_time < tolerance);
+ }));
}
- {
- m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < 5; ++i)
- v.push_back(support::make_test_thread(f2));
- std::this_thread::sleep_for(WaitTime + Tolerance);
- m.unlock();
- for (auto& t : v)
- t.join();
+
+ // Wait for all the threads to be ready to take the lock before we unlock it from here, otherwise
+ // there's a high chance that we're not testing the "locking an already locked" mutex use case.
+ // There is still technically a race condition here.
+ while (ready < 5)
+ /* spin */;
+ std::this_thread::sleep_for(wait_time / 5);
+
+ m.unlock(); // this should allow the threads to lock-shared 'm'
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Try to lock-shared an already-locked mutex for a short amount of time and fail.
+ // Again, this is technically flaky but we use such long durations that it should work.
+ {
+ std::chrono::milliseconds const wait_time(10);
+ std::chrono::milliseconds const tolerance(750); // in case the thread we spawned goes to sleep or something
+
+ std::shared_timed_mutex m;
+ m.lock();
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ auto elapsed = measure([&] {
+ bool succeeded = m.try_lock_shared_for(wait_time);
+ assert(!succeeded);
+ });
+
+ // Ensure we failed within some bounded time.
+ assert(elapsed - wait_time < tolerance);
+ }));
}
+ for (auto& t : threads)
+ t.join();
+
+ m.unlock();
+ }
+
return 0;
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_until.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_until.pass.cpp
index a95ffab..af88bae 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_until.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_shared_until.pass.cpp
@@ -5,12 +5,10 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_timed_mutex;
@@ -18,73 +16,109 @@
// template <class Clock, class Duration>
// bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
-#include <thread>
-
+#include <shared_mutex>
#include <atomic>
#include <cassert>
#include <chrono>
-#include <cstdlib>
-#include <shared_mutex>
+#include <thread>
#include <vector>
#include "make_test_thread.h"
-#include "test_macros.h"
-
-std::shared_timed_mutex m;
-
-typedef std::chrono::steady_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
-
-ms SuccessWaitTime = ms(5000); // Some machines are busy or slow or both
-ms FailureWaitTime = ms(50);
-
-// On busy or slow machines, there can be a significant delay between thread
-// creation and thread start, so we use an atomic variable to signal that the
-// thread is actually executing.
-static std::atomic<unsigned> countDown;
-
-void f1()
-{
- --countDown;
- time_point t0 = Clock::now();
- assert(m.try_lock_shared_until(Clock::now() + SuccessWaitTime) == true);
- time_point t1 = Clock::now();
- m.unlock_shared();
- assert(t1 - t0 <= SuccessWaitTime);
-}
-void f2()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_shared_until(Clock::now() + FailureWaitTime) == false);
- assert(Clock::now() - t0 >= FailureWaitTime);
+template <class Function>
+std::chrono::microseconds measure(Function f) {
+ std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
+ f();
+ std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
+ return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}
-int main(int, char**)
-{
- int threads = 5;
+int main(int, char**) {
+ // Try to lock-shared a mutex that is not locked yet. This should succeed immediately.
+ {
+ std::shared_timed_mutex m;
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ bool succeeded = m.try_lock_shared_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(1));
+ assert(succeeded);
+ m.unlock_shared();
+ }));
+ }
+
+ for (auto& t : threads)
+ t.join();
+ }
+
+ // Try to lock-shared an already-locked mutex for a long enough amount of time and succeed.
+ // This is technically flaky, but we use such long durations that it should pass even
+ // in slow or contended environments.
{
- countDown.store(threads);
+ std::chrono::milliseconds const wait_time(500);
+ std::chrono::milliseconds const tolerance = wait_time * 3;
+ std::atomic<int> ready(0);
+
+ std::shared_timed_mutex m;
m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < threads; ++i)
- v.push_back(support::make_test_thread(f1));
- while (countDown > 0)
- std::this_thread::yield();
- m.unlock();
- for (auto& t : v)
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ ++ready;
+ while (ready < 5)
+ /* spin until all threads are created */;
+
+ auto elapsed = measure([&] {
+ bool succeeded = m.try_lock_shared_until(std::chrono::steady_clock::now() + wait_time);
+ assert(succeeded);
+ m.unlock_shared();
+ });
+
+ // Ensure we didn't wait significantly longer than our timeout. This is technically
+ // flaky and non-conforming because an implementation is free to block for arbitrarily
+ // long, but any decent quality implementation should pass this test.
+ assert(elapsed - wait_time < tolerance);
+ }));
+ }
+
+ // Wait for all the threads to be ready to take the lock before we unlock it from here, otherwise
+ // there's a high chance that we're not testing the "locking an already locked" mutex use case.
+ // There is still technically a race condition here.
+ while (ready < 5)
+ /* spin */;
+ std::this_thread::sleep_for(wait_time / 5);
+
+ m.unlock(); // this should allow the threads to lock-shared 'm'
+
+ for (auto& t : threads)
t.join();
}
+
+ // Try to lock-shared an already-locked mutex for a short amount of time and fail.
+ // Again, this is technically flaky but we use such long durations that it should work.
{
+ std::chrono::milliseconds const wait_time(10);
+ std::chrono::milliseconds const tolerance(750); // in case the thread we spawned goes to sleep or something
+
+ std::shared_timed_mutex m;
m.lock();
- std::vector<std::thread> v;
- for (int i = 0; i < threads; ++i)
- v.push_back(support::make_test_thread(f2));
- for (auto& t : v)
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 5; ++i) {
+ threads.push_back(support::make_test_thread([&] {
+ auto elapsed = measure([&] {
+ bool succeeded = m.try_lock_shared_until(std::chrono::steady_clock::now() + wait_time);
+ assert(!succeeded);
+ });
+
+ // Ensure we failed within some bounded time.
+ assert(elapsed - wait_time < tolerance);
+ }));
+ }
+
+ for (auto& t : threads)
t.join();
+
m.unlock();
}
diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_until.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_until.pass.cpp
index fb521ef..948364d 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_until.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/try_lock_until.pass.cpp
@@ -5,12 +5,10 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
+
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11
-// ALLOW_RETRIES: 2
-
// <shared_mutex>
// class shared_timed_mutex;
@@ -19,69 +17,89 @@
// bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
#include <shared_mutex>
-#include <thread>
-#include <cstdlib>
+#include <atomic>
#include <cassert>
#include <chrono>
+#include <thread>
#include "make_test_thread.h"
-#include "test_macros.h"
-std::shared_timed_mutex m;
+template <class Function>
+std::chrono::microseconds measure(Function f) {
+ std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
+ f();
+ std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
+ return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+}
-typedef std::chrono::steady_clock Clock;
-typedef Clock::time_point time_point;
-typedef Clock::duration duration;
-typedef std::chrono::milliseconds ms;
-typedef std::chrono::nanoseconds ns;
+int main(int, char**) {
+ // Try to lock a mutex that is not locked yet. This should succeed immediately.
+ {
+ std::shared_timed_mutex m;
+ bool succeeded = m.try_lock_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(1));
+ assert(succeeded);
+ m.unlock();
+ }
+ // Try to lock an already-locked mutex for a long enough amount of time and succeed.
+ // This is technically flaky, but we use such long durations that it should pass even
+ // in slow or contended environments.
+ {
+ std::chrono::milliseconds const wait_time(500);
+ std::chrono::milliseconds const tolerance = wait_time * 3;
+ std::atomic<bool> ready(false);
-ms WaitTime = ms(250);
+ std::shared_timed_mutex m;
+ m.lock();
-// Thread sanitizer causes more overhead and will sometimes cause this test
-// to fail. To prevent this we give Thread sanitizer more time to complete the
-// test.
-#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
-ms Tolerance = ms(50);
-#else
-ms Tolerance = ms(50 * 5);
-#endif
+ std::thread t = support::make_test_thread([&] {
+ auto elapsed = measure([&] {
+ ready = true;
+ bool succeeded = m.try_lock_until(std::chrono::steady_clock::now() + wait_time);
+ assert(succeeded);
+ m.unlock();
+ });
-void f1()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_until(Clock::now() + WaitTime + Tolerance) == true);
- time_point t1 = Clock::now();
- m.unlock();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within tolerance
-}
+ // Ensure we didn't wait significantly longer than our timeout. This is technically
+ // flaky and non-conforming because an implementation is free to block for arbitrarily
+ // long, but any decent quality implementation should pass this test.
+ assert(elapsed - wait_time < tolerance);
+ });
-void f2()
-{
- time_point t0 = Clock::now();
- assert(m.try_lock_until(Clock::now() + WaitTime) == false);
- time_point t1 = Clock::now();
- ns d = t1 - t0 - WaitTime;
- assert(d < Tolerance); // within tolerance
-}
+ // Wait for the thread to be ready to take the lock before we unlock it from here, otherwise
+ // there's a high chance that we're not testing the "locking an already locked" mutex use case.
+ // There is still technically a race condition here.
+ while (!ready)
+ /* spin */;
+ std::this_thread::sleep_for(wait_time / 5);
-int main(int, char**)
-{
- {
- m.lock();
- std::thread t = support::make_test_thread(f1);
- std::this_thread::sleep_for(WaitTime);
- m.unlock();
- t.join();
- }
- {
- m.lock();
- std::thread t = support::make_test_thread(f2);
- std::this_thread::sleep_for(WaitTime + Tolerance);
- m.unlock();
- t.join();
- }
+ m.unlock(); // this should allow the thread to lock 'm'
+ t.join();
+ }
+
+ // Try to lock an already-locked mutex for a short amount of time and fail.
+ // Again, this is technically flaky but we use such long durations that it should work.
+ {
+ std::chrono::milliseconds const wait_time(10);
+ std::chrono::milliseconds const tolerance(750); // in case the thread we spawned goes to sleep or something
+
+ std::shared_timed_mutex m;
+ m.lock();
+
+ std::thread t = support::make_test_thread([&] {
+ auto elapsed = measure([&] {
+ bool succeeded = m.try_lock_until(std::chrono::steady_clock::now() + wait_time);
+ assert(!succeeded);
+ });
+
+ // Ensure we failed within some bounded time.
+ assert(elapsed - wait_time < tolerance);
+ });
+
+ t.join();
+
+ m.unlock();
+ }
return 0;
}