xref: /llvm-project/libcxx/test/std/thread/thread.condition/thread.condition.condvar/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;
14 
15 // template <class Predicate>
16 //   void wait(unique_lock<mutex>& 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 
main(int,char **)27 int main(int, char**) {
28   // Test unblocking via a call to notify_one() in another thread.
29   //
30   // To test this, we try to minimize the likelihood that we got awoken by a
31   // spurious wakeup by updating the likely_spurious flag only immediately
32   // before we perform the notification.
33   {
34     std::atomic<bool> ready(false);
35     std::atomic<bool> likely_spurious(true);
36     std::condition_variable cv;
37     std::mutex mutex;
38 
39     std::thread t1 = support::make_test_thread([&] {
40       std::unique_lock<std::mutex> lock(mutex);
41       ready = true;
42       cv.wait(lock, [&] { return !likely_spurious; });
43     });
44 
45     std::thread t2 = support::make_test_thread([&] {
46       while (!ready) {
47         // spin
48       }
49 
50       // Acquire the same mutex as t1. This ensures that the condition variable has started
51       // waiting (and hence released that mutex).
52       std::unique_lock<std::mutex> lock(mutex);
53 
54       likely_spurious = false;
55       lock.unlock();
56       cv.notify_one();
57     });
58 
59     t2.join();
60     t1.join();
61   }
62 
63   // Test unblocking via a spurious wakeup.
64   //
65   // To test this, we basically never wake up the condition variable. This way, we
66   // are hoping to get out of the wait via a spurious wakeup.
67   //
68   // However, since spurious wakeups are not required to even happen, this test is
69   // only trying to trigger that code path, but not actually asserting that it is
70   // taken. In particular, we do need to eventually ensure we get out of the wait
71   // by standard means, so we actually wake up the thread at the end.
72   {
73     std::atomic<bool> ready(false);
74     std::atomic<bool> awoken(false);
75     std::condition_variable cv;
76     std::mutex mutex;
77 
78     std::thread t1 = support::make_test_thread([&] {
79       std::unique_lock<std::mutex> lock(mutex);
80       ready = true;
81       cv.wait(lock, [&] { return true; });
82       awoken = true;
83     });
84 
85     std::thread t2 = support::make_test_thread([&] {
86       while (!ready) {
87         // spin
88       }
89 
90       // Acquire the same mutex as t1. This ensures that the condition variable has started
91       // waiting (and hence released that mutex).
92       std::unique_lock<std::mutex> lock(mutex);
93       lock.unlock();
94 
95       // Give some time for t1 to be awoken spuriously so that code path is used.
96       std::this_thread::sleep_for(std::chrono::seconds(1));
97 
98       // We would want to assert that the thread has been awoken after this time,
99       // however nothing guarantees us that it ever gets spuriously awoken, so
100       // we can't really check anything. This is still left here as documentation.
101       bool woke = awoken.load();
102       assert(woke || !woke);
103 
104       // Whatever happened, actually awaken the condition variable to ensure the test finishes.
105       cv.notify_one();
106     });
107 
108     t2.join();
109     t1.join();
110   }
111 
112   return 0;
113 }
114