From 29ef92b9051bca3a35be0cbc5e03d787941f945e Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Wed, 31 Jul 2024 08:27:01 -0500 Subject: [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. --- .../assign.compile.pass.cpp | 21 +++ .../thread.shared_mutex.class/assign.verify.cpp | 27 --- .../thread.shared_mutex.class/copy.verify.cpp | 26 --- .../ctor.copy.compile.pass.cpp | 21 +++ .../ctor.default.pass.cpp | 27 +++ .../thread.shared_mutex.class/default.pass.cpp | 28 --- .../thread.shared_mutex.class/lock.pass.cpp | 110 ++++++++---- .../thread.shared_mutex.class/lock_shared.pass.cpp | 172 +++++++++++------- .../thread.shared_mutex.class/try_lock.pass.cpp | 66 +++---- .../try_lock_shared.pass.cpp | 93 +++++----- .../assign.compile.fail.cpp | 26 --- .../assign.compile.pass.cpp | 21 +++ .../copy.compile.fail.cpp | 25 --- .../ctor.copy.compile.pass.cpp | 21 +++ .../ctor.default.pass.cpp | 27 +++ .../thread.sharedtimedmutex.class/default.pass.cpp | 27 --- .../thread.sharedtimedmutex.class/lock.pass.cpp | 122 ++++++++----- .../lock_shared.pass.cpp | 194 ++++++++++++--------- .../try_lock.pass.cpp | 66 +++---- .../try_lock_for.pass.cpp | 125 +++++++------ .../try_lock_shared.pass.cpp | 92 +++++----- .../try_lock_shared_for.pass.cpp | 159 ++++++++++------- .../try_lock_shared_until.pass.cpp | 142 +++++++++------ .../try_lock_until.pass.cpp | 126 +++++++------ 24 files changed, 1020 insertions(+), 744 deletions(-) create mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp delete mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.verify.cpp delete mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.verify.cpp create mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp create mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.default.pass.cpp delete mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp delete mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.fail.cpp create mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp delete mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/copy.compile.fail.cpp create mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp create mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp delete mode 100644 libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/default.pass.cpp diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp new file mode 100644 index 0000000..0d90bff --- /dev/null +++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.compile.pass.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14 + +// + +// class shared_mutex; + +// shared_mutex& operator=(const shared_mutex&) = delete; + +#include +#include + +static_assert(!std::is_copy_assignable::value, ""); 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.verify.cpp deleted file mode 100644 index 34164aa..0000000 --- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.verify.cpp +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// UNSUPPORTED: c++03, c++11, c++14 - -// - -// class shared_mutex; - -// shared_mutex& operator=(const shared_mutex&) = delete; - -#include - -int main(int, char**) -{ - std::shared_mutex m0; - std::shared_mutex m1; - m1 = m0; // expected-error {{overload resolution selected deleted operator '='}} - - return 0; -} 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/copy.verify.cpp deleted file mode 100644 index 9b43198..0000000 --- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.verify.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// UNSUPPORTED: c++03, c++11, c++14 - -// - -// class shared_mutex; - -// shared_mutex(const shared_mutex&) = delete; - -#include - -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; -} diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp new file mode 100644 index 0000000..f9e1935 --- /dev/null +++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.copy.compile.pass.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14 + +// + +// class shared_mutex; + +// shared_mutex(const shared_mutex&) = delete; + +#include +#include + +static_assert(!std::is_copy_constructible::value, ""); diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.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 new file mode 100644 index 0000000..c941f3a --- /dev/null +++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/ctor.default.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14 + +// + +// class shared_mutex; + +// shared_mutex(); + +#include + +#include "test_macros.h" + +int main(int, char**) { + std::shared_mutex m; + (void)m; + + return 0; +} 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/default.pass.cpp deleted file mode 100644 index 5504645..0000000 --- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// UNSUPPORTED: c++03, c++11, c++14 - -// - -// class shared_mutex; - -// shared_mutex(); - -#include - -#include "test_macros.h" - -int main(int, char**) -{ - std::shared_mutex m; - (void)m; - - 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 - // // class shared_mutex; // void lock(); +#include +#include #include #include -#include -#include #include +#include #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 ready(false); + std::shared_mutex m; + m.lock(); + std::atomic 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 ready(false); + std::shared_mutex m; + m.lock_shared(); + std::atomic 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 counter = 0; + std::shared_mutex mutex; + + std::vector 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 - // // class shared_mutex; // void lock_shared(); -#include -#include -#include #include +#include +#include +#include #include #include #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 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 ready(0); + std::shared_mutex m; m.lock(); - std::vector v; - for (int i = 0; i < 5; ++i) - v.push_back(support::make_test_thread(f)); - std::this_thread::sleep_for(WaitTime); + std::atomic is_locked_from_main(true); + + std::vector 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 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 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 threads; + constexpr int n_threads = 5; + std::atomic holders = 0; + int concurrent_holders[n_threads] = {}; + std::atomic 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 - // // class shared_mutex; // bool try_lock(); +#include +#include #include #include -#include -#include #include #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 - // // class shared_mutex; // bool try_lock_shared(); -#include -#include -#include #include +#include #include #include #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 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 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 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 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.fail.cpp deleted file mode 100644 index c2cd893..0000000 --- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.fail.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11 - -// - -// class shared_timed_mutex; - -// shared_timed_mutex& operator=(const shared_timed_mutex&) = delete; - -#include - -int main(int, char**) -{ - std::shared_timed_mutex m0; - std::shared_timed_mutex m1; - m1 = m0; - - return 0; -} diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp new file mode 100644 index 0000000..9cbb0b1 --- /dev/null +++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/assign.compile.pass.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11 + +// + +// class shared_timed_mutex; + +// shared_timed_mutex& operator=(const shared_timed_mutex&) = delete; + +#include +#include + +static_assert(!std::is_copy_assignable::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/copy.compile.fail.cpp deleted file mode 100644 index 9b0a661..0000000 --- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/copy.compile.fail.cpp +++ /dev/null @@ -1,25 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11 - -// - -// class shared_timed_mutex; - -// shared_timed_mutex(const shared_timed_mutex&) = delete; - -#include - -int main(int, char**) -{ - std::shared_timed_mutex m0; - std::shared_timed_mutex m1(m0); - - return 0; -} diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp new file mode 100644 index 0000000..12b01a5 --- /dev/null +++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.copy.compile.pass.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11 + +// + +// class shared_timed_mutex; + +// shared_timed_mutex(const shared_timed_mutex&) = delete; + +#include +#include + +static_assert(!std::is_copy_constructible::value, ""); diff --git a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp new file mode 100644 index 0000000..eadc59e --- /dev/null +++ b/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/ctor.default.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11 + +// + +// class shared_timed_mutex; + +// shared_timed_mutex(); + +#include + +#include "test_macros.h" + +int main(int, char**) { + std::shared_timed_mutex m; + (void)m; + + return 0; +} 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/default.pass.cpp deleted file mode 100644 index 7a8d096..0000000 --- a/libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.sharedtimedmutex.requirements/thread.sharedtimedmutex.class/default.pass.cpp +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// UNSUPPORTED: c++03, c++11 - -// - -// class shared_timed_mutex; - -// shared_timed_mutex(); - -#include - -#include "test_macros.h" - -int main(int, char**) { - std::shared_timed_mutex m; - (void)m; - - return 0; -} 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 - // // class shared_timed_mutex; // void lock(); +#include #include #include #include -#include -#include #include +#include #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 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 ready(false); + std::shared_timed_mutex m; + m.lock(); + std::atomic 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 ready(false); + std::shared_timed_mutex m; + m.lock_shared(); + std::atomic 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 counter(0); + std::shared_timed_mutex mutex; + + std::vector 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 - // // class shared_timed_mutex; // void lock_shared(); - +#include +#include #include #include -#include -#include -#include #include #include #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 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 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 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 ready(0); + std::shared_timed_mutex m; + m.lock(); + std::atomic is_locked_from_main(true); + + std::vector 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 ready(0); + std::shared_timed_mutex m; + m.lock_shared(); + + std::vector 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 threads; + constexpr int n_threads = 5; + std::atomic holders(0); + int concurrent_holders[n_threads] = {}; + std::atomic 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 - // // class shared_timed_mutex; // bool try_lock(); +#include +#include #include #include -#include -#include #include #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 // @@ -18,69 +17,89 @@ // bool try_lock_for(const chrono::duration& rel_time); #include -#include -#include +#include #include #include +#include #include "make_test_thread.h" -#include "test_macros.h" -std::shared_timed_mutex m; +template +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(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 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 - // // class shared_timed_mutex; // bool try_lock_shared(); -#include -#include -#include #include +#include #include #include #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 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 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 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 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 - // // class shared_timed_mutex; @@ -19,75 +17,110 @@ // bool try_lock_shared_for(const chrono::duration& rel_time); #include -#include -#include -#include +#include #include #include +#include +#include #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 +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(end - start); } -int main(int, char**) -{ - { - m.lock(); - std::vector 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 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 ready(0); + + std::shared_timed_mutex m; + m.lock(); + + std::vector 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 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 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 - // // class shared_timed_mutex; @@ -18,73 +16,109 @@ // template // bool try_lock_shared_until(const chrono::time_point& abs_time); -#include - +#include #include #include #include -#include -#include +#include #include #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 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 +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(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 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 ready(0); + + std::shared_timed_mutex m; m.lock(); - std::vector 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 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 v; - for (int i = 0; i < threads; ++i) - v.push_back(support::make_test_thread(f2)); - for (auto& t : v) + + std::vector 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 - // // class shared_timed_mutex; @@ -19,69 +17,89 @@ // bool try_lock_until(const chrono::time_point& abs_time); #include -#include -#include +#include #include #include +#include #include "make_test_thread.h" -#include "test_macros.h" -std::shared_timed_mutex m; +template +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(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 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; } -- cgit v1.1