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> |
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 | |
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 | |