From 0807902c0feaec85b07387406ccf1a51827b5ac7 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Wed, 8 May 2024 16:29:14 -0400 Subject: [PATCH 1/4] [libc++] Refactor tests for std::condition_variable These tests have always been flaky, which led us to using ALLOW_RETRIES on them. However, while investigating #89083 (using Github provided macOS builders), these tests surfaced as being basically unworkably flaky in that environment. This patch solves that problem by refactoring the tests to make them succeed deterministically. --- .../wait_for.pass.cpp | 146 ++++++----- .../wait_for_pred.pass.cpp | 201 +++++++++----- .../wait_pred.pass.cpp | 129 ++++++--- .../wait_until.pass.cpp | 176 ++++++------- .../wait_until_pred.pass.cpp | 215 +++++++++------ .../wait_for.pass.cpp | 161 +++++++----- .../wait_for_pred.pass.cpp | 213 +++++++++------ .../wait_pred.pass.cpp | 134 +++++++--- .../wait_until.pass.cpp | 183 +++++++------ .../wait_until_pred.pass.cpp | 246 +++++++++++------- 10 files changed, 1104 insertions(+), 700 deletions(-) diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp index 42150207c3c4d..d70e027a702c4 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp @@ -5,9 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 + +// UNSUPPORTED: no-threads, c++03 // @@ -19,77 +18,92 @@ // const chrono::duration& rel_time); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -std::condition_variable cv; -std::mutex mut; - -int test1 = 0; -int test2 = 0; - -bool expect_timeout = false; - -void f() -{ - typedef std::chrono::system_clock Clock; - typedef std::chrono::milliseconds milliseconds; - std::unique_lock lk(mut); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - Clock::time_point wait_end = t0 + milliseconds(250); - Clock::duration d; - do { - d = wait_end - Clock::now(); - if (d <= milliseconds(0)) break; - } while (test2 == 0 && cv.wait_for(lk, d) == std::cv_status::no_timeout); - Clock::time_point t1 = Clock::now(); - if (!expect_timeout) - { - assert(t1 - t0 < milliseconds(250)); - assert(test2 != 0); - } - else - { - assert(t1 - t0 - milliseconds(250) < milliseconds(50)); - assert(test2 == 0); - } +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**) -{ - { - std::unique_lock lk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - expect_timeout = true; - { - std::unique_lock lk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } +int main(int, char**) { + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_for() and we wait + // again in case we get awoken spuriously. Note that it can actually + // happen that we get awoken spuriously and fail to recognize it + // (making this test useless), but the likelihood should be small. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = std::chrono::seconds(3600); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + auto elapsed = measure([&] { + ready = true; + do { + std::cv_status result = cv.wait_for(lock, timeout); + assert(result == std::cv_status::no_timeout); + } while (likely_spurious); + }); + + // This can technically fail if we have many spurious awakenings, but in practice the + // tolerance is so high that it shouldn't be a problem. + assert(elapsed < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This blocks the condition variable inside its wait call + // so we can notify it while it is waiting. + std::unique_lock lock(mutex); + cv.notify_one(); + likely_spurious = false; + lock.unlock(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable + // with a certain timeout, and we never awaken it. To guard against + // spurious wakeups, we wait again whenever we are awoken for a reason + // other than a timeout. + { + auto timeout = std::chrono::milliseconds(250); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + std::cv_status result; + do { + auto elapsed = measure([&] { result = cv.wait_for(lock, timeout); }); + if (result == std::cv_status::timeout) + assert(elapsed >= timeout); + } while (result != std::cv_status::timeout); + }); + + t1.join(); + } return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp index 872bcb6d8a57d..31e28463ecef2 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp @@ -5,9 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 + +// UNSUPPORTED: no-threads, c++03 // @@ -20,82 +19,142 @@ // Predicate pred); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -class Pred -{ - int& i_; -public: - explicit Pred(int& i) : i_(i) {} - - bool operator()() {return i_ != 0;} -}; - -std::condition_variable cv; -std::mutex mut; - -int test1 = 0; -int test2 = 0; - -int runs = 0; - -void f() -{ - typedef std::chrono::system_clock Clock; - typedef std::chrono::milliseconds milliseconds; - std::unique_lock lk(mut); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - bool r = cv.wait_for(lk, milliseconds(250), Pred(test2)); - ((void)r); // Prevent unused warning - Clock::time_point t1 = Clock::now(); - if (runs == 0) - { - assert(t1 - t0 < milliseconds(250)); - assert(test2 != 0); - } - else - { - assert(t1 - t0 - milliseconds(250) < milliseconds(50)); - assert(test2 == 0); - } - ++runs; +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**) -{ - { - std::unique_locklk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - { - std::unique_locklk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } +int main(int, char**) { + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_for() and we try to minimize + // the likelihood that we got awoken by a spurious wakeup by updating the + // likely_spurious flag only immediately before we perform the notification. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = std::chrono::seconds(3600); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + auto elapsed = measure([&] { + ready = true; + bool result = cv.wait_for(lock, timeout, [&] { return !likely_spurious; }); + assert(result); // return value should be true since we didn't time out + }); + assert(elapsed < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + std::unique_lock lock(mutex); + lock.unlock(); + + likely_spurious = false; + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable with a certain + // timeout, and we never awaken it. The "stop waiting" predicate always returns false, + // which means that we can't get out of the wait via a spurious wakeup. + { + auto timeout = std::chrono::milliseconds(250); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + auto elapsed = measure([&] { + bool result = cv.wait_for(lock, timeout, [] { return false; }); // never stop waiting (until timeout) + assert(!result); // return value should be false since the predicate returns false after the timeout + }); + assert(elapsed >= timeout); + }); + + t1.join(); + } + + // Test unblocking via a spurious wakeup. + // + // To test this, we set a fairly long timeout in wait_for() and we basically never + // wake up the condition variable. This way, we are hoping to get out of the wait + // via a spurious wakeup. + // + // However, since spurious wakeups are not required to even happen, this test is + // only trying to trigger that code path, but not actually asserting that it is + // taken. In particular, we do need to eventually ensure we get out of the wait + // by standard means, so we actually wake up the thread at the end. + { + std::atomic ready = false; + std::atomic awoken = false; + auto timeout = std::chrono::seconds(3600); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + auto elapsed = measure([&] { + ready = true; + bool result = cv.wait_for(lock, timeout, [&] { return true; }); + awoken = true; + assert(result); // return value should be true since we didn't time out + }); + assert(elapsed < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + std::unique_lock lock(mutex); + lock.unlock(); + + // Give some time for t1 to be awoken spuriously so that code path is used. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // We would want to assert that the thread has been awoken after this time, + // however nothing guarantees us that it ever gets spuriously awoken, so + // we can't really check anything. This is still left here as documentation. + assert(awoken || !awoken); + + // Whatever happened, actually awaken the condition variable to ensure the test + // doesn't keep running until the timeout. + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp index 15feba55616b0..b99d14a9cc355 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp @@ -5,9 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 + +// UNSUPPORTED: no-threads, c++03 // @@ -17,51 +16,99 @@ // void wait(unique_lock& lock, Predicate pred); #include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -std::condition_variable cv; -std::mutex mut; - -int test1 = 0; -int test2 = 0; - -class Pred -{ - int& i_; -public: - explicit Pred(int& i) : i_(i) {} - - bool operator()() {return i_ != 0;} -}; - -void f() -{ - std::unique_lock lk(mut); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - cv.wait(lk, Pred(test2)); - assert(test2 != 0); -} +int main(int, char**) { + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we try to minimize the likelihood that we got awoken by a + // spurious wakeup by updating the likely_spurious flag only immediately + // before we perform the notification. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + ready = true; + cv.wait(lock, [&] { return !likely_spurious; }); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + std::unique_lock lock(mutex); + lock.unlock(); + + likely_spurious = false; + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a spurious wakeup. + // + // To test this, we basically never wake up the condition variable. This way, we + // are hoping to get out of the wait via a spurious wakeup. + // + // However, since spurious wakeups are not required to even happen, this test is + // only trying to trigger that code path, but not actually asserting that it is + // taken. In particular, we do need to eventually ensure we get out of the wait + // by standard means, so we actually wake up the thread at the end. + { + std::atomic ready = false; + std::atomic awoken = false; + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + ready = true; + cv.wait(lock, [&] { return true; }); + awoken = true; + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + std::unique_lock lock(mutex); + lock.unlock(); + + // Give some time for t1 to be awoken spuriously so that code path is used. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // We would want to assert that the thread has been awoken after this time, + // however nothing guarantees us that it ever gets spuriously awoken, so + // we can't really check anything. This is still left here as documentation. + assert(awoken || !awoken); + + // Whatever happened, actually awaken the condition variable to ensure the test finishes. + cv.notify_one(); + }); -int main(int, char**) -{ - std::unique_locklk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); + t2.join(); + t1.join(); + } return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp index 03205e68dca67..cab043941c25d 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp @@ -5,9 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 + +// UNSUPPORTED: no-threads, c++03 // @@ -19,100 +18,101 @@ // const chrono::time_point& abs_time); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -struct TestClock -{ - typedef std::chrono::milliseconds duration; - typedef duration::rep rep; - typedef duration::period period; - typedef std::chrono::time_point time_point; - static const bool is_steady = true; - - static time_point now() - { - using namespace std::chrono; - return time_point(duration_cast( - steady_clock::now().time_since_epoch() - )); - } +struct TestClock { + typedef std::chrono::milliseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point time_point; + static const bool is_steady = true; + + static time_point now() { + using namespace std::chrono; + return time_point(duration_cast(steady_clock::now().time_since_epoch())); + } }; -std::condition_variable cv; -std::mutex mut; - -int test1 = 0; -int test2 = 0; - -int runs = 0; - -template -void f() -{ - std::unique_lock lk(mut); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - typename Clock::time_point t0 = Clock::now(); - typename Clock::time_point t = t0 + std::chrono::milliseconds(250); - while (test2 == 0 && cv.wait_until(lk, t) == std::cv_status::no_timeout) - ; - typename Clock::time_point t1 = Clock::now(); - if (runs == 0) - { - assert(t1 - t0 < std::chrono::milliseconds(250)); - assert(test2 != 0); - } - else - { - assert(t1 - t0 - std::chrono::milliseconds(250) < std::chrono::milliseconds(50)); - assert(test2 == 0); - } - ++runs; -} +template +void test() { + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_until() and we wait + // again in case we get awoken spuriously. Note that it can actually + // happen that we get awoken spuriously and fail to recognize it + // (making this test useless), but the likelihood should be small. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = Clock::now() + std::chrono::seconds(3600); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + ready = true; + do { + std::cv_status result = cv.wait_until(lock, timeout); + assert(result == std::cv_status::no_timeout); + } while (likely_spurious); + + // This can technically fail if we have many spurious awakenings, but in practice the + // tolerance is so high that it shouldn't be a problem. + assert(Clock::now() < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This blocks the condition variable inside its wait call + // so we can notify it while it is waiting. + std::unique_lock lock(mutex); + cv.notify_one(); + likely_spurious = false; + lock.unlock(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable + // with a certain timeout, and we never awaken it. To guard against + // spurious wakeups, we wait again whenever we are awoken for a reason + // other than a timeout. + { + auto timeout = Clock::now() + std::chrono::milliseconds(250); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + std::cv_status result; + do { + result = cv.wait_until(lock, timeout); + if (result == std::cv_status::timeout) + assert(Clock::now() >= timeout); + } while (result != std::cv_status::timeout); + }); -template -void run_test() -{ - runs = 0; - test1 = 0; - test2 = 0; - { - std::unique_locklk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - { - std::unique_locklk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } + t1.join(); + } } -int main(int, char**) -{ - run_test(); - run_test(); - run_test(); - return 0; +int main(int, char**) { + test(); + test(); + test(); + return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp index fb8bd6e380693..c5441f98ccb15 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 +// UNSUPPORTED: no-threads, c++03 // @@ -20,99 +19,147 @@ // Predicate pred); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -struct Clock -{ - typedef std::chrono::milliseconds duration; - typedef duration::rep rep; - typedef duration::period period; - typedef std::chrono::time_point time_point; - static const bool is_steady = true; - - static time_point now() - { - using namespace std::chrono; - return time_point(duration_cast( - steady_clock::now().time_since_epoch() - )); - } +struct TestClock { + typedef std::chrono::milliseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point time_point; + static const bool is_steady = true; + + static time_point now() { + using namespace std::chrono; + return time_point(duration_cast(steady_clock::now().time_since_epoch())); + } }; -class Pred -{ - int& i_; -public: - explicit Pred(int& i) : i_(i) {} +template +void test() { + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_until() and we try to minimize + // the likelihood that we got awoken by a spurious wakeup by updating the + // likely_spurious flag only immediately before we perform the notification. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = Clock::now() + std::chrono::seconds(3600); + std::condition_variable cv; + std::mutex mutex; - bool operator()() {return i_ != 0;} -}; + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + ready = true; + bool result = cv.wait_until(lock, timeout, [&] { return !likely_spurious; }); + assert(result); // return value should be true since we didn't time out + assert(Clock::now() < timeout); + }); -std::condition_variable cv; -std::mutex mut; - -int test1 = 0; -int test2 = 0; - -int runs = 0; - -void f() -{ - std::unique_lock lk(mut); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - Clock::time_point t = t0 + Clock::duration(250); - bool r = cv.wait_until(lk, t, Pred(test2)); - Clock::time_point t1 = Clock::now(); - if (runs == 0) - { - assert(t1 - t0 < Clock::duration(250)); - assert(test2 != 0); - assert(r); - } - else - { - assert(t1 - t0 - Clock::duration(250) < Clock::duration(50)); - assert(test2 == 0); - assert(!r); - } - ++runs; -} + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + std::unique_lock lock(mutex); + lock.unlock(); + + likely_spurious = false; + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable with a certain + // timeout, and we never awaken it. The "stop waiting" predicate always returns false, + // which means that we can't get out of the wait via a spurious wakeup. + { + auto timeout = Clock::now() + std::chrono::milliseconds(250); + std::condition_variable cv; + std::mutex mutex; -int main(int, char**) -{ - { - std::unique_lock lk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - { - std::unique_lock lk(mut); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + bool result = cv.wait_until(lock, timeout, [] { return false; }); // never stop waiting (until timeout) + assert(!result); // return value should be false since the predicate returns false after the timeout + assert(Clock::now() >= timeout); + }); + + t1.join(); + } + + // Test unblocking via a spurious wakeup. + // + // To test this, we set a fairly long timeout in wait_until() and we basically never + // wake up the condition variable. This way, we are hoping to get out of the wait + // via a spurious wakeup. + // + // However, since spurious wakeups are not required to even happen, this test is + // only trying to trigger that code path, but not actually asserting that it is + // taken. In particular, we do need to eventually ensure we get out of the wait + // by standard means, so we actually wake up the thread at the end. + { + std::atomic ready = false; + std::atomic awoken = false; + auto timeout = Clock::now() + std::chrono::seconds(3600); + std::condition_variable cv; + std::mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + std::unique_lock lock(mutex); + ready = true; + bool result = cv.wait_until(lock, timeout, [&] { return true; }); + awoken = true; + assert(result); // return value should be true since we didn't time out + assert(Clock::now() < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + std::unique_lock lock(mutex); + lock.unlock(); + + // Give some time for t1 to be awoken spuriously so that code path is used. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // We would want to assert that the thread has been awoken after this time, + // however nothing guarantees us that it ever gets spuriously awoken, so + // we can't really check anything. This is still left here as documentation. + assert(awoken || !awoken); + + // Whatever happened, actually awaken the condition variable to ensure the test + // doesn't keep running until the timeout. + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } +} +int main(int, char**) { + test(); + test(); + test(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp index 95acef90470ec..7110917d1a8b3 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 +// UNSUPPORTED: no-threads, c++03 // @@ -18,81 +17,105 @@ // wait_for(Lock& lock, const chrono::duration& rel_time); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -std::condition_variable_any cv; - -typedef std::timed_mutex L0; -typedef std::unique_lock L1; - -L0 m0; - -int test1 = 0; -int test2 = 0; - -bool expect_timeout = false; - -void f() -{ - typedef std::chrono::system_clock Clock; - typedef std::chrono::milliseconds milliseconds; - L1 lk(m0); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - Clock::time_point wait_end = t0 + milliseconds(250); - Clock::duration d; - do { - d = wait_end - Clock::now(); - if (d <= milliseconds(0)) break; - } while (test2 == 0 && cv.wait_for(lk, d) == std::cv_status::no_timeout); - Clock::time_point t1 = Clock::now(); - if (!expect_timeout) - { - assert(t1 - t0 < milliseconds(250)); - assert(test2 != 0); - } - else - { - assert(t1 - t0 - milliseconds(250) < milliseconds(50)); - assert(test2 == 0); - } +template +struct MyLock : std::unique_lock { + using std::unique_lock::unique_lock; +}; + +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**) -{ - { - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - expect_timeout = true; - { - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } +template +void test() { + using Mutex = typename Lock::mutex_type; + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_for() and we wait + // again in case we get awoken spuriously. Note that it can actually + // happen that we get awoken spuriously and fail to recognize it + // (making this test useless), but the likelihood should be small. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = std::chrono::seconds(3600); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + auto elapsed = measure([&] { + ready = true; + do { + std::cv_status result = cv.wait_for(lock, timeout); + assert(result == std::cv_status::no_timeout); + } while (likely_spurious); + }); + + // This can technically fail if we have many spurious awakenings, but in practice the + // tolerance is so high that it shouldn't be a problem. + assert(elapsed < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This blocks the condition variable inside its wait call + // so we can notify it while it is waiting. + Lock lock(mutex); + cv.notify_one(); + likely_spurious = false; + lock.unlock(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable + // with a certain timeout, and we never awaken it. To guard against + // spurious wakeups, we wait again whenever we are awoken for a reason + // other than a timeout. + { + auto timeout = std::chrono::milliseconds(250); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + std::cv_status result; + do { + auto elapsed = measure([&] { result = cv.wait_for(lock, timeout); }); + if (result == std::cv_status::timeout) + assert(elapsed >= timeout); + } while (result != std::cv_status::timeout); + }); + + t1.join(); + } +} +int main(int, char**) { + test>(); + test>(); + test>(); + test>(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp index 0b560022bc67c..17fcdbdc7603c 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 +// UNSUPPORTED: no-threads, c++03 // @@ -19,89 +18,149 @@ // Predicate pred); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -class Pred -{ - int& i_; -public: - explicit Pred(int& i) : i_(i) {} - - bool operator()() {return i_ != 0;} +template +struct MyLock : std::unique_lock { + using std::unique_lock::unique_lock; }; -std::condition_variable_any cv; - -typedef std::timed_mutex L0; -typedef std::unique_lock L1; - -L0 m0; - -int test1 = 0; -int test2 = 0; - -int runs = 0; -bool expect_result = false; - -void f() -{ - typedef std::chrono::system_clock Clock; - typedef std::chrono::milliseconds milliseconds; - L1 lk(m0); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - bool result = cv.wait_for(lk, milliseconds(250), Pred(test2)); - assert(result == expect_result); - Clock::time_point t1 = Clock::now(); - if (runs == 0) - { - assert(t1 - t0 < milliseconds(250)); - assert(test2 != 0); - } - else - { - assert(t1 - t0 - milliseconds(250) < milliseconds(50)); - assert(test2 == 0); - } - ++runs; +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**) -{ - { - expect_result = true; - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - { - expect_result = false; - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } - - return 0; +template +void test() { + using Mutex = typename Lock::mutex_type; + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_for() and we try to minimize + // the likelihood that we got awoken by a spurious wakeup by updating the + // likely_spurious flag only immediately before we perform the notification. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = std::chrono::seconds(3600); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + auto elapsed = measure([&] { + ready = true; + bool result = cv.wait_for(lock, timeout, [&] { return !likely_spurious; }); + assert(result); // return value should be true since we didn't time out + }); + assert(elapsed < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + Lock lock(mutex); + lock.unlock(); + + likely_spurious = false; + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable with a certain + // timeout, and we never awaken it. The "stop waiting" predicate always returns false, + // which means that we can't get out of the wait via a spurious wakeup. + { + auto timeout = std::chrono::milliseconds(250); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + auto elapsed = measure([&] { + bool result = cv.wait_for(lock, timeout, [] { return false; }); // never stop waiting (until timeout) + assert(!result); // return value should be false since the predicate returns false after the timeout + }); + assert(elapsed >= timeout); + }); + + t1.join(); + } + + // Test unblocking via a spurious wakeup. + // + // To test this, we set a fairly long timeout in wait_for() and we basically never + // wake up the condition variable. This way, we are hoping to get out of the wait + // via a spurious wakeup. + // + // However, since spurious wakeups are not required to even happen, this test is + // only trying to trigger that code path, but not actually asserting that it is + // taken. In particular, we do need to eventually ensure we get out of the wait + // by standard means, so we actually wake up the thread at the end. + { + std::atomic ready = false; + std::atomic awoken = false; + auto timeout = std::chrono::seconds(3600); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + auto elapsed = measure([&] { + ready = true; + bool result = cv.wait_for(lock, timeout, [&] { return true; }); + awoken = true; + assert(result); // return value should be true since we didn't time out + }); + assert(elapsed < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + Lock lock(mutex); + lock.unlock(); + + // Give some time for t1 to be awoken spuriously so that code path is used. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // We would want to assert that the thread has been awoken after this time, + // however nothing guarantees us that it ever gets spuriously awoken, so + // we can't really check anything. This is still left here as documentation. + assert(awoken || !awoken); + + // Whatever happened, actually awaken the condition variable to ensure the test + // doesn't keep running until the timeout. + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } } + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp index a5e28137bef8c..bb48ab9b34c49 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp @@ -5,9 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 + +// UNSUPPORTED: no-threads, c++03 // @@ -17,55 +16,114 @@ // void wait(Lock& lock, Predicate pred); #include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -std::condition_variable_any cv; +template +struct MyLock : std::unique_lock { + using std::unique_lock::unique_lock; +}; -typedef std::timed_mutex L0; -typedef std::unique_lock L1; +template +void test() { + using Mutex = typename Lock::mutex_type; -L0 m0; + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we try to minimize the likelihood that we got awoken by a + // spurious wakeup by updating the likely_spurious flag only immediately + // before we perform the notification. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + std::condition_variable_any cv; + Mutex mutex; -int test1 = 0; -int test2 = 0; + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + ready = true; + cv.wait(lock, [&] { return !likely_spurious; }); + }); -class Pred -{ - int& i_; -public: - explicit Pred(int& i) : i_(i) {} + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } - bool operator()() {return i_ != 0;} -}; + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + Lock lock(mutex); + lock.unlock(); + + likely_spurious = false; + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a spurious wakeup. + // + // To test this, we basically never wake up the condition variable. This way, we + // are hoping to get out of the wait via a spurious wakeup. + // + // However, since spurious wakeups are not required to even happen, this test is + // only trying to trigger that code path, but not actually asserting that it is + // taken. In particular, we do need to eventually ensure we get out of the wait + // by standard means, so we actually wake up the thread at the end. + { + std::atomic ready = false; + std::atomic awoken = false; + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + ready = true; + cv.wait(lock, [&] { return true; }); + awoken = true; + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + Lock lock(mutex); + lock.unlock(); + + // Give some time for t1 to be awoken spuriously so that code path is used. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // We would want to assert that the thread has been awoken after this time, + // however nothing guarantees us that it ever gets spuriously awoken, so + // we can't really check anything. This is still left here as documentation. + assert(awoken || !awoken); + + // Whatever happened, actually awaken the condition variable to ensure the test finishes. + cv.notify_one(); + }); -void f() -{ - L1 lk(m0); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - cv.wait(lk, Pred(test2)); - assert(test2 != 0); + t2.join(); + t1.join(); + } } -int main(int, char**) -{ - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); +int main(int, char**) { + test>(); + test>(); + test>(); + test>(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp index 0f2334393d833..86471b11278db 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 +// UNSUPPORTED: no-threads, c++03 // @@ -18,93 +17,119 @@ // wait_until(Lock& lock, const chrono::time_point& abs_time); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -struct Clock -{ - typedef std::chrono::milliseconds duration; - typedef duration::rep rep; - typedef duration::period period; - typedef std::chrono::time_point time_point; - static const bool is_steady = true; - - static time_point now() - { - using namespace std::chrono; - return time_point(duration_cast( - steady_clock::now().time_since_epoch() - )); - } +struct TestClock { + typedef std::chrono::milliseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point time_point; + static const bool is_steady = true; + + static time_point now() { + using namespace std::chrono; + return time_point(duration_cast(steady_clock::now().time_since_epoch())); + } }; -std::condition_variable_any cv; - -typedef std::timed_mutex L0; -typedef std::unique_lock L1; - -L0 m0; - -int test1 = 0; -int test2 = 0; - -int runs = 0; - -void f() -{ - L1 lk(m0); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - Clock::time_point t = t0 + Clock::duration(250); - while (test2 == 0 && cv.wait_until(lk, t) == std::cv_status::no_timeout) - ; - Clock::time_point t1 = Clock::now(); - if (runs == 0) - { - assert(t1 - t0 < Clock::duration(250)); - assert(test2 != 0); - } - else - { - assert(t1 - t0 - Clock::duration(250) < Clock::duration(50)); - assert(test2 == 0); - } - ++runs; +template +struct MyLock : std::unique_lock { + using std::unique_lock::unique_lock; +}; + +template +void test() { + using Mutex = typename Lock::mutex_type; + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_until() and we wait + // again in case we get awoken spuriously. Note that it can actually + // happen that we get awoken spuriously and fail to recognize it + // (making this test useless), but the likelihood should be small. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = Clock::now() + std::chrono::seconds(3600); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + ready = true; + do { + std::cv_status result = cv.wait_until(lock, timeout); + assert(result == std::cv_status::no_timeout); + } while (likely_spurious); + + // This can technically fail if we have many spurious awakenings, but in practice the + // tolerance is so high that it shouldn't be a problem. + assert(Clock::now() < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This blocks the condition variable inside its wait call + // so we can notify it while it is waiting. + Lock lock(mutex); + cv.notify_one(); + likely_spurious = false; + lock.unlock(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable + // with a certain timeout, and we never awaken it. To guard against + // spurious wakeups, we wait again whenever we are awoken for a reason + // other than a timeout. + { + auto timeout = Clock::now() + std::chrono::milliseconds(250); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + std::cv_status result; + do { + result = cv.wait_until(lock, timeout); + if (result == std::cv_status::timeout) + assert(Clock::now() >= timeout); + } while (result != std::cv_status::timeout); + }); + + t1.join(); + } } -int main(int, char**) -{ - { - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - { - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } +int main(int, char**) { + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp index aa60ae4715df6..a27539326a3f0 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// // -// UNSUPPORTED: no-threads -// ALLOW_RETRIES: 2 +// UNSUPPORTED: no-threads, c++03 // @@ -20,103 +19,176 @@ // Predicate pred); #include +#include +#include +#include #include #include -#include -#include #include "make_test_thread.h" #include "test_macros.h" -struct Clock -{ - typedef std::chrono::milliseconds duration; - typedef duration::rep rep; - typedef duration::period period; - typedef std::chrono::time_point time_point; - static const bool is_steady = true; - - static time_point now() - { - using namespace std::chrono; - return time_point(duration_cast( - steady_clock::now().time_since_epoch() - )); - } +struct TestClock { + typedef std::chrono::milliseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point time_point; + static const bool is_steady = true; + + static time_point now() { + using namespace std::chrono; + return time_point(duration_cast(steady_clock::now().time_since_epoch())); + } }; -class Pred -{ - int& i_; -public: - explicit Pred(int& i) : i_(i) {} - - bool operator()() {return i_ != 0;} +template +struct MyLock : std::unique_lock { + using std::unique_lock::unique_lock; }; -std::condition_variable_any cv; - -typedef std::timed_mutex L0; -typedef std::unique_lock L1; - -L0 m0; - -int test1 = 0; -int test2 = 0; - -int runs = 0; - -void f() -{ - L1 lk(m0); - assert(test2 == 0); - test1 = 1; - cv.notify_one(); - Clock::time_point t0 = Clock::now(); - Clock::time_point t = t0 + Clock::duration(250); - bool r = cv.wait_until(lk, t, Pred(test2)); - Clock::time_point t1 = Clock::now(); - if (runs == 0) - { - assert(t1 - t0 < Clock::duration(250)); - assert(test2 != 0); - assert(r); - } - else - { - assert(t1 - t0 - Clock::duration(250) < Clock::duration(50)); - assert(test2 == 0); - assert(!r); - } - ++runs; +template +void test() { + using Mutex = typename Lock::mutex_type; + // Test unblocking via a call to notify_one() in another thread. + // + // To test this, we set a very long timeout in wait_until() and we try to minimize + // the likelihood that we got awoken by a spurious wakeup by updating the + // likely_spurious flag only immediately before we perform the notification. + { + std::atomic ready = false; + std::atomic likely_spurious = true; + auto timeout = Clock::now() + std::chrono::seconds(3600); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + ready = true; + bool result = cv.wait_until(lock, timeout, [&] { return !likely_spurious; }); + assert(result); // return value should be true since we didn't time out + assert(Clock::now() < timeout); + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + Lock lock(mutex); + lock.unlock(); + + likely_spurious = false; + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } + + // Test unblocking via a timeout. + // + // To test this, we create a thread that waits on a condition variable with a certain + // timeout, and we never awaken it. The "stop waiting" predicate always returns false, + // which means that we can't get out of the wait via a spurious wakeup. + { + auto timeout = Clock::now() + std::chrono::milliseconds(250); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + bool result = cv.wait_until(lock, timeout, [] { return false; }); // never stop waiting (until timeout) + assert(!result); // return value should be false since the predicate returns false after the timeout + assert(Clock::now() >= timeout); + }); + + t1.join(); + } + + // Test unblocking via a spurious wakeup. + // + // To test this, we set a fairly long timeout in wait_until() and we basically never + // wake up the condition variable. This way, we are hoping to get out of the wait + // via a spurious wakeup. + // + // However, since spurious wakeups are not required to even happen, this test is + // only trying to trigger that code path, but not actually asserting that it is + // taken. In particular, we do need to eventually ensure we get out of the wait + // by standard means, so we actually wake up the thread at the end. + { + std::atomic ready = false; + std::atomic awoken = false; + auto timeout = Clock::now() + std::chrono::seconds(3600); + std::condition_variable_any cv; + Mutex mutex; + + std::thread t1 = support::make_test_thread([&] { + Lock lock(mutex); + ready = true; + bool result = cv.wait_until(lock, timeout, [&] { return true; }); + awoken = true; + assert(result); // return value should be true since we didn't time out + assert(Clock::now() < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely + }); + + std::thread t2 = support::make_test_thread([&] { + while (!ready) { + // spin + } + + // Acquire the same mutex as t1. This ensures that the condition variable has started + // waiting (and hence released that mutex). We don't actually need to hold the lock, we + // simply use it as a signal that the condition variable has started waiting. + Lock lock(mutex); + lock.unlock(); + + // Give some time for t1 to be awoken spuriously so that code path is used. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // We would want to assert that the thread has been awoken after this time, + // however nothing guarantees us that it ever gets spuriously awoken, so + // we can't really check anything. This is still left here as documentation. + assert(awoken || !awoken); + + // Whatever happened, actually awaken the condition variable to ensure the test + // doesn't keep running until the timeout. + cv.notify_one(); + }); + + t2.join(); + t1.join(); + } } -int main(int, char**) -{ - { - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - test2 = 1; - lk.unlock(); - cv.notify_one(); - t.join(); - } - test1 = 0; - test2 = 0; - { - L1 lk(m0); - std::thread t = support::make_test_thread(f); - assert(test1 == 0); - while (test1 == 0) - cv.wait(lk); - assert(test1 != 0); - lk.unlock(); - t.join(); - } +int main(int, char**) { + // Run on multiple threads to speed up the test, and because it ought to work anyways. + std::thread tests[] = { + support::make_test_thread([] { + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + }), + support::make_test_thread([] { + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + }), + support::make_test_thread([] { + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + }), + support::make_test_thread([] { + test, TestClock>(); + test, std::chrono::steady_clock>(); + test, std::chrono::system_clock>(); + })}; + + for (std::thread& t : tests) + t.join(); return 0; } From 15cf6aaeb3b71389ca43876b8a26f6203f3f8d75 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 10 May 2024 10:57:38 -0400 Subject: [PATCH 2/4] Review comments --- .../wait_for_pred.pass.cpp | 11 +++++------ .../thread.condition.condvar/wait_pred.pass.cpp | 11 +++++------ .../thread.condition.condvar/wait_until.pass.cpp | 1 - .../wait_until_pred.pass.cpp | 12 +++++------- .../wait_for_pred.pass.cpp | 11 +++++------ .../wait_pred.pass.cpp | 11 +++++------ .../wait_until.pass.cpp | 4 ---- .../wait_until_pred.pass.cpp | 15 +++++---------- 8 files changed, 30 insertions(+), 46 deletions(-) diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp index 31e28463ecef2..f9d5f1e76c814 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp @@ -65,12 +65,11 @@ int main(int, char**) { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). std::unique_lock lock(mutex); - lock.unlock(); likely_spurious = false; + lock.unlock(); cv.notify_one(); }); @@ -134,8 +133,7 @@ int main(int, char**) { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). std::unique_lock lock(mutex); lock.unlock(); @@ -145,7 +143,8 @@ int main(int, char**) { // We would want to assert that the thread has been awoken after this time, // however nothing guarantees us that it ever gets spuriously awoken, so // we can't really check anything. This is still left here as documentation. - assert(awoken || !awoken); + bool woke = awoken.load(); + assert(woke || !woke); // Whatever happened, actually awaken the condition variable to ensure the test // doesn't keep running until the timeout. diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp index b99d14a9cc355..5f04a6d1e96fd 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp @@ -48,12 +48,11 @@ int main(int, char**) { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). std::unique_lock lock(mutex); - lock.unlock(); likely_spurious = false; + lock.unlock(); cv.notify_one(); }); @@ -89,8 +88,7 @@ int main(int, char**) { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). std::unique_lock lock(mutex); lock.unlock(); @@ -100,7 +98,8 @@ int main(int, char**) { // We would want to assert that the thread has been awoken after this time, // however nothing guarantees us that it ever gets spuriously awoken, so // we can't really check anything. This is still left here as documentation. - assert(awoken || !awoken); + bool woke = awoken.load(); + assert(woke || !woke); // Whatever happened, actually awaken the condition variable to ensure the test finishes. cv.notify_one(); diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp index cab043941c25d..2654fb05f45b6 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp @@ -113,6 +113,5 @@ void test() { int main(int, char**) { test(); test(); - test(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp index c5441f98ccb15..614e433205253 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp @@ -69,12 +69,11 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). std::unique_lock lock(mutex); - lock.unlock(); likely_spurious = false; + lock.unlock(); cv.notify_one(); }); @@ -134,8 +133,7 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). std::unique_lock lock(mutex); lock.unlock(); @@ -145,7 +143,8 @@ void test() { // We would want to assert that the thread has been awoken after this time, // however nothing guarantees us that it ever gets spuriously awoken, so // we can't really check anything. This is still left here as documentation. - assert(awoken || !awoken); + bool woke = awoken.load(); + assert(woke || !woke); // Whatever happened, actually awaken the condition variable to ensure the test // doesn't keep running until the timeout. @@ -160,6 +159,5 @@ void test() { int main(int, char**) { test(); test(); - test(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp index 17fcdbdc7603c..0faf2eeda2ff7 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp @@ -71,12 +71,11 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). Lock lock(mutex); - lock.unlock(); likely_spurious = false; + lock.unlock(); cv.notify_one(); }); @@ -140,8 +139,7 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). Lock lock(mutex); lock.unlock(); @@ -151,7 +149,8 @@ void test() { // We would want to assert that the thread has been awoken after this time, // however nothing guarantees us that it ever gets spuriously awoken, so // we can't really check anything. This is still left here as documentation. - assert(awoken || !awoken); + bool woke = awoken.load(); + assert(woke || !woke); // Whatever happened, actually awaken the condition variable to ensure the test // doesn't keep running until the timeout. diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp index bb48ab9b34c49..c9c0e9c67c65a 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp @@ -56,12 +56,11 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). Lock lock(mutex); - lock.unlock(); likely_spurious = false; + lock.unlock(); cv.notify_one(); }); @@ -97,8 +96,7 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). Lock lock(mutex); lock.unlock(); @@ -108,7 +106,8 @@ void test() { // We would want to assert that the thread has been awoken after this time, // however nothing guarantees us that it ever gets spuriously awoken, so // we can't really check anything. This is still left here as documentation. - assert(awoken || !awoken); + bool woke = awoken.load(); + assert(woke || !woke); // Whatever happened, actually awaken the condition variable to ensure the test finishes. cv.notify_one(); diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp index 86471b11278db..6705a03945768 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp @@ -118,18 +118,14 @@ void test() { int main(int, char**) { test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); return 0; } diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp index a27539326a3f0..cfb8454e0c890 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp @@ -75,12 +75,11 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). Lock lock(mutex); - lock.unlock(); likely_spurious = false; + lock.unlock(); cv.notify_one(); }); @@ -140,8 +139,7 @@ void test() { } // Acquire the same mutex as t1. This ensures that the condition variable has started - // waiting (and hence released that mutex). We don't actually need to hold the lock, we - // simply use it as a signal that the condition variable has started waiting. + // waiting (and hence released that mutex). Lock lock(mutex); lock.unlock(); @@ -151,7 +149,8 @@ void test() { // We would want to assert that the thread has been awoken after this time, // however nothing guarantees us that it ever gets spuriously awoken, so // we can't really check anything. This is still left here as documentation. - assert(awoken || !awoken); + bool woke = awoken.load(); + assert(woke || !woke); // Whatever happened, actually awaken the condition variable to ensure the test // doesn't keep running until the timeout. @@ -169,22 +168,18 @@ int main(int, char**) { support::make_test_thread([] { test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); }), support::make_test_thread([] { test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); }), support::make_test_thread([] { test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); }), support::make_test_thread([] { test, TestClock>(); test, std::chrono::steady_clock>(); - test, std::chrono::system_clock>(); })}; for (std::thread& t : tests) From 2661e374a45a84a86125b5b29914765c322ca445 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 10 May 2024 13:51:09 -0400 Subject: [PATCH 3/4] Fix C++11 --- .../thread.condition.condvar/wait_for.pass.cpp | 4 ++-- .../thread.condition.condvar/wait_for_pred.pass.cpp | 12 ++++++------ .../thread.condition.condvar/wait_pred.pass.cpp | 8 ++++---- .../thread.condition.condvar/wait_until.pass.cpp | 6 +++--- .../wait_until_pred.pass.cpp | 12 ++++++------ .../thread.condition.condvarany/wait_for.pass.cpp | 6 +++--- .../wait_for_pred.pass.cpp | 12 ++++++------ .../thread.condition.condvarany/wait_pred.pass.cpp | 8 ++++---- .../thread.condition.condvarany/wait_until.pass.cpp | 6 +++--- .../wait_until_pred.pass.cpp | 12 ++++++------ 10 files changed, 43 insertions(+), 43 deletions(-) diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp index d70e027a702c4..d69746b22d5c2 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp @@ -43,8 +43,8 @@ int main(int, char**) { // happen that we get awoken spuriously and fail to recognize it // (making this test useless), but the likelihood should be small. { - std::atomic ready = false; - std::atomic likely_spurious = true; + std::atomic ready (false); + std::atomic likely_spurious (true); auto timeout = std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp index f9d5f1e76c814..76fc7393bc8fc 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for_pred.pass.cpp @@ -43,9 +43,9 @@ int main(int, char**) { // the likelihood that we got awoken by a spurious wakeup by updating the // likely_spurious flag only immediately before we perform the notification. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex; @@ -110,9 +110,9 @@ int main(int, char**) { // taken. In particular, we do need to eventually ensure we get out of the wait // by standard means, so we actually wake up the thread at the end. { - std::atomic ready = false; - std::atomic awoken = false; - auto timeout = std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic awoken(false); + auto timeout = std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp index 5f04a6d1e96fd..5ce5bccb37f1a 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_pred.pass.cpp @@ -31,8 +31,8 @@ int main(int, char**) { // spurious wakeup by updating the likely_spurious flag only immediately // before we perform the notification. { - std::atomic ready = false; - std::atomic likely_spurious = true; + std::atomic ready(false); + std::atomic likely_spurious(true); std::condition_variable cv; std::mutex mutex; @@ -70,8 +70,8 @@ int main(int, char**) { // taken. In particular, we do need to eventually ensure we get out of the wait // by standard means, so we actually wake up the thread at the end. { - std::atomic ready = false; - std::atomic awoken = false; + std::atomic ready(false); + std::atomic awoken(false); std::condition_variable cv; std::mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp index 2654fb05f45b6..6f3a5a01cdd1e 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp @@ -49,9 +49,9 @@ void test() { // happen that we get awoken spuriously and fail to recognize it // (making this test useless), but the likelihood should be small. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = Clock::now() + std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = Clock::now() + std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp index 614e433205253..847d0c10c5725 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_until_pred.pass.cpp @@ -49,9 +49,9 @@ void test() { // the likelihood that we got awoken by a spurious wakeup by updating the // likely_spurious flag only immediately before we perform the notification. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = Clock::now() + std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = Clock::now() + std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex; @@ -112,9 +112,9 @@ void test() { // taken. In particular, we do need to eventually ensure we get out of the wait // by standard means, so we actually wake up the thread at the end. { - std::atomic ready = false; - std::atomic awoken = false; - auto timeout = Clock::now() + std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic awoken(false); + auto timeout = Clock::now() + std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp index 7110917d1a8b3..eab38081d7b7f 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp @@ -49,9 +49,9 @@ void test() { // happen that we get awoken spuriously and fail to recognize it // (making this test useless), but the likelihood should be small. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = std::chrono::seconds(3600); std::condition_variable_any cv; Mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp index 0faf2eeda2ff7..2dc36938b41e9 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp @@ -49,9 +49,9 @@ void test() { // the likelihood that we got awoken by a spurious wakeup by updating the // likely_spurious flag only immediately before we perform the notification. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = std::chrono::seconds(3600); std::condition_variable_any cv; Mutex mutex; @@ -116,9 +116,9 @@ void test() { // taken. In particular, we do need to eventually ensure we get out of the wait // by standard means, so we actually wake up the thread at the end. { - std::atomic ready = false; - std::atomic awoken = false; - auto timeout = std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic awoken(false); + auto timeout = std::chrono::seconds(3600); std::condition_variable_any cv; Mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp index c9c0e9c67c65a..48efbf12e7386 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp @@ -39,8 +39,8 @@ void test() { // spurious wakeup by updating the likely_spurious flag only immediately // before we perform the notification. { - std::atomic ready = false; - std::atomic likely_spurious = true; + std::atomic ready(false); + std::atomic likely_spurious(true); std::condition_variable_any cv; Mutex mutex; @@ -78,8 +78,8 @@ void test() { // taken. In particular, we do need to eventually ensure we get out of the wait // by standard means, so we actually wake up the thread at the end. { - std::atomic ready = false; - std::atomic awoken = false; + std::atomic ready(false); + std::atomic awoken(false); std::condition_variable_any cv; Mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp index 6705a03945768..6494bcd6dbe39 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp @@ -54,9 +54,9 @@ void test() { // happen that we get awoken spuriously and fail to recognize it // (making this test useless), but the likelihood should be small. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = Clock::now() + std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = Clock::now() + std::chrono::seconds(3600); std::condition_variable_any cv; Mutex mutex; diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp index cfb8454e0c890..ee7c1729aacfa 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp @@ -55,9 +55,9 @@ void test() { // the likelihood that we got awoken by a spurious wakeup by updating the // likely_spurious flag only immediately before we perform the notification. { - std::atomic ready = false; - std::atomic likely_spurious = true; - auto timeout = Clock::now() + std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = Clock::now() + std::chrono::seconds(3600); std::condition_variable_any cv; Mutex mutex; @@ -118,9 +118,9 @@ void test() { // taken. In particular, we do need to eventually ensure we get out of the wait // by standard means, so we actually wake up the thread at the end. { - std::atomic ready = false; - std::atomic awoken = false; - auto timeout = Clock::now() + std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic awoken(false); + auto timeout = Clock::now() + std::chrono::seconds(3600); std::condition_variable_any cv; Mutex mutex; From 609af87e003a2693c0e055ec9a70608e8cdafec9 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 10 May 2024 14:19:19 -0400 Subject: [PATCH 4/4] Fix formatting --- .../thread.condition.condvar/wait_for.pass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp index d69746b22d5c2..6a054f74b9fb9 100644 --- a/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvar/wait_for.pass.cpp @@ -43,9 +43,9 @@ int main(int, char**) { // happen that we get awoken spuriously and fail to recognize it // (making this test useless), but the likelihood should be small. { - std::atomic ready (false); - std::atomic likely_spurious (true); - auto timeout = std::chrono::seconds(3600); + std::atomic ready(false); + std::atomic likely_spurious(true); + auto timeout = std::chrono::seconds(3600); std::condition_variable cv; std::mutex mutex;