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 |
10 | |
11 | // notify_all_at_thread_exit(...) requires move semantics to transfer the unique_lock. |
12 | // UNSUPPORTED: c++03 |
13 | |
14 | // The fix of LWG3343 is done in the dylib. That means Apple backdeployment |
15 | // targets remain broken. Due to the nature of the test, testing on a broken |
16 | // system does not guarantee that the test fails, so the test can't use XFAIL. |
17 | // UNSUPPORTED: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{.+}} |
18 | // UNSUPPORTED: stdlib=apple-libc++ && target={{.+}}-apple-macosx11.{{.+}} |
19 | |
20 | // This is a regression test for LWG3343. |
21 | // |
22 | // <condition_variable> |
23 | // |
24 | // void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk); |
25 | |
26 | #include "make_test_thread.h" |
27 | #include "test_macros.h" |
28 | |
29 | #include <condition_variable> |
30 | #include <cassert> |
31 | #include <chrono> |
32 | #include <memory> |
33 | #include <mutex> |
34 | #include <thread> |
35 | |
36 | int condition_variable_lock_skipped_counter = 0; |
37 | |
38 | TEST_DIAGNOSTIC_PUSH |
39 | // MSVC warning C4583: 'X::cv_': destructor is not implicitly called |
40 | TEST_MSVC_DIAGNOSTIC_IGNORED(4583) |
41 | |
42 | union X { |
43 | X() : cv_() {} |
44 | ~X() {} |
45 | std::condition_variable cv_; |
46 | unsigned char bytes_[sizeof(std::condition_variable)]; |
47 | }; |
48 | |
49 | TEST_DIAGNOSTIC_POP |
50 | |
51 | void test() |
52 | { |
53 | constexpr int N = 3; |
54 | |
55 | X x; |
56 | std::mutex m; |
57 | int threads_active = N; |
58 | |
59 | for (int i = 0; i < N; ++i) { |
60 | std::thread t = support::make_test_thread([&] { |
61 | // Emulate work being done. |
62 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
63 | |
64 | // Signal thread completion. |
65 | std::unique_lock<std::mutex> lk(m); |
66 | --threads_active; |
67 | std::notify_all_at_thread_exit(x.cv_, std::move(lk)); |
68 | }); |
69 | t.detach(); |
70 | } |
71 | |
72 | // Wait until all threads complete, i.e. until they've all |
73 | // decremented `threads_active` and then unlocked `m` at thread exit. |
74 | // It is possible that this `wait` may spuriously wake up, |
75 | // but it won't be able to continue until the last thread |
76 | // unlocks `m`. |
77 | { |
78 | std::unique_lock<std::mutex> lk(m); |
79 | // Due to OS scheduling the workers might have terminated when this |
80 | // code is reached. In that case the wait will not sleep and the call |
81 | // to notify_all_at_thread_exit has no effect; the condition variable |
82 | // will not be used here. |
83 | // |
84 | // Keep track of how often that happens, if too often the test needs |
85 | // to be improved. |
86 | if(threads_active == 0) |
87 | ++condition_variable_lock_skipped_counter; |
88 | x.cv_.wait(lock&: lk, p: [&]() { return threads_active == 0; }); |
89 | } |
90 | |
91 | // Destroy the condition_variable and shred the bytes. |
92 | // Simulate reusing the memory for something else. |
93 | x.cv_.~condition_variable(); |
94 | for (unsigned char& c : x.bytes_) { |
95 | c = 0xcd; |
96 | } |
97 | |
98 | DoNotOptimize(x.bytes_); |
99 | |
100 | // Check that the bytes still have the same value we just wrote to them. |
101 | // If any thread wrongly unlocked `m` before calling cv.notify_all(), and |
102 | // cv.notify_all() writes to the memory of the cv, then we have a chance |
103 | // to detect the problem here. |
104 | int sum = 0; |
105 | for (unsigned char c : x.bytes_) { |
106 | sum += c; |
107 | } |
108 | DoNotOptimize(sum); |
109 | assert(sum == (0xcd * sizeof(std::condition_variable))); |
110 | } |
111 | |
112 | int main(int, char**) |
113 | { |
114 | for (int i = 0; i < 1000; ++i) { |
115 | test(); |
116 | } |
117 | |
118 | // The threshold is arbitrary, it just makes sure the notification is |
119 | // tested a reasonable number of times. |
120 | assert(condition_variable_lock_skipped_counter < 250); |
121 | |
122 | return 0; |
123 | } |
124 | |