xref: /llvm-project/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_pred.pass.cpp (revision ac88ad3c805f0cc0ea85975d52b2037940b2d040)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: no-threads, c++03
10 
11 // <condition_variable>
12 
13 // class condition_variable_any;
14 
15 // template <class Lock, class Predicate>
16 //   void wait(Lock& lock, Predicate pred);
17 
18 #include <condition_variable>
19 #include <atomic>
20 #include <cassert>
21 #include <mutex>
22 #include <thread>
23 
24 #include "make_test_thread.h"
25 #include "test_macros.h"
26 
27 template <class Mutex>
28 struct MyLock : std::unique_lock<Mutex> {
29   using std::unique_lock<Mutex>::unique_lock;
30 };
31 
32 template <class Lock>
test()33 void test() {
34   using Mutex = typename Lock::mutex_type;
35 
36   // Test unblocking via a call to notify_one() in another thread.
37   //
38   // To test this, we try to minimize the likelihood that we got awoken by a
39   // spurious wakeup by updating the likely_spurious flag only immediately
40   // before we perform the notification.
41   {
42     std::atomic<bool> ready(false);
43     std::atomic<bool> likely_spurious(true);
44     std::condition_variable_any cv;
45     Mutex mutex;
46 
47     std::thread t1 = support::make_test_thread([&] {
48       Lock lock(mutex);
49       ready = true;
50       cv.wait(lock, [&] { return !likely_spurious; });
51     });
52 
53     std::thread t2 = support::make_test_thread([&] {
54       while (!ready) {
55         // spin
56       }
57 
58       // Acquire the same mutex as t1. This ensures that the condition variable has started
59       // waiting (and hence released that mutex).
60       Lock lock(mutex);
61 
62       likely_spurious = false;
63       lock.unlock();
64       cv.notify_one();
65     });
66 
67     t2.join();
68     t1.join();
69   }
70 
71   // Test unblocking via a spurious wakeup.
72   //
73   // To test this, we basically never wake up the condition variable. This way, we
74   // are hoping to get out of the wait via a spurious wakeup.
75   //
76   // However, since spurious wakeups are not required to even happen, this test is
77   // only trying to trigger that code path, but not actually asserting that it is
78   // taken. In particular, we do need to eventually ensure we get out of the wait
79   // by standard means, so we actually wake up the thread at the end.
80   {
81     std::atomic<bool> ready(false);
82     std::atomic<bool> awoken(false);
83     std::condition_variable_any cv;
84     Mutex mutex;
85 
86     std::thread t1 = support::make_test_thread([&] {
87       Lock lock(mutex);
88       ready = true;
89       cv.wait(lock, [&] { return true; });
90       awoken = true;
91     });
92 
93     std::thread t2 = support::make_test_thread([&] {
94       while (!ready) {
95         // spin
96       }
97 
98       // Acquire the same mutex as t1. This ensures that the condition variable has started
99       // waiting (and hence released that mutex).
100       Lock lock(mutex);
101       lock.unlock();
102 
103       // Give some time for t1 to be awoken spuriously so that code path is used.
104       std::this_thread::sleep_for(std::chrono::seconds(1));
105 
106       // We would want to assert that the thread has been awoken after this time,
107       // however nothing guarantees us that it ever gets spuriously awoken, so
108       // we can't really check anything. This is still left here as documentation.
109       bool woke = awoken.load();
110       assert(woke || !woke);
111 
112       // Whatever happened, actually awaken the condition variable to ensure the test finishes.
113       cv.notify_one();
114     });
115 
116     t2.join();
117     t1.join();
118   }
119 }
120 
main(int,char **)121 int main(int, char**) {
122   test<std::unique_lock<std::mutex>>();
123   test<std::unique_lock<std::timed_mutex>>();
124   test<MyLock<std::mutex>>();
125   test<MyLock<std::timed_mutex>>();
126 
127   return 0;
128 }
129