1 // Copyright (C) 2019-2021 Free Software Foundation, Inc.
2 //
3 // This file is part of the GNU ISO C++ Library. This library is free
4 // software; you can redistribute it and/or modify it under the
5 // terms of the GNU General Public License as published by the
6 // Free Software Foundation; either version 3, or (at your option)
7 // any later version.
8
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13
14 // You should have received a copy of the GNU General Public License along
15 // with this library; see the file COPYING3. If not see
16 // <http://www.gnu.org/licenses/>.
17
18 // { dg-options "-std=gnu++2a -pthread" }
19 // { dg-do run { target c++2a } }
20 // { dg-add-options libatomic }
21 // { dg-additional-options "-pthread" { target pthread } }
22 // { dg-require-gthreads "" }
23
24 #include <thread>
25 #include <chrono>
26 #include <atomic>
27 #include <testsuite_hooks.h>
28
29 using namespace std::literals;
30
31 //------------------------------------------------------
32
test_no_stop_token()33 void test_no_stop_token()
34 {
35 // test the basic jthread API (not taking stop_token arg)
36
37 VERIFY(std::jthread::hardware_concurrency() == std::thread::hardware_concurrency());
38 std::stop_token stoken;
39 VERIFY(!stoken.stop_possible());
40 {
41 std::jthread::id t1ID{std::this_thread::get_id()};
42 std::atomic<bool> t1AllSet{false};
43 std::jthread t1([&t1ID, &t1AllSet] {
44 t1ID = std::this_thread::get_id();
45 t1AllSet.store(true);
46 for (int c='9'; c>='0'; --c) {
47 std::this_thread::sleep_for(222ms);
48 }
49 });
50 for (int i=0; !t1AllSet.load(); ++i) {
51 std::this_thread::sleep_for(10ms);
52 }
53 VERIFY(t1.joinable());
54 VERIFY(t1ID == t1.get_id());
55 stoken = t1.get_stop_token();
56 VERIFY(!stoken.stop_requested());
57 }
58 VERIFY(stoken.stop_requested());
59 }
60
61 //------------------------------------------------------
62
test_stop_token()63 void test_stop_token()
64 {
65 // test the basic thread API (taking stop_token arg)
66
67 std::stop_source ssource;
68 std::stop_source origsource;
69 VERIFY(ssource.stop_possible());
70 VERIFY(!ssource.stop_requested());
71 {
72 std::jthread::id t1ID{std::this_thread::get_id()};
73 std::atomic<bool> t1AllSet{false};
74 std::atomic<bool> t1done{false};
75 std::jthread t1([&t1ID, &t1AllSet, &t1done] (std::stop_token st) {
76 // check some values of the started thread:
77 t1ID = std::this_thread::get_id();
78 t1AllSet.store(true);
79 for (int i=0; !st.stop_requested(); ++i) {
80 std::this_thread::sleep_for(100ms);
81 }
82 t1done.store(true);
83 },
84 ssource.get_token());
85 for (int i=0; !t1AllSet.load(); ++i) {
86 std::this_thread::sleep_for(10ms);
87 }
88 // and check all values:
89 VERIFY(t1.joinable());
90 VERIFY(t1ID == t1.get_id());
91
92 std::this_thread::sleep_for(470ms);
93 origsource = std::move(ssource);
94 ssource = t1.get_stop_source();
95 VERIFY(!ssource.stop_requested());
96 auto ret = ssource.request_stop();
97 VERIFY(ret);
98 ret = ssource.request_stop();
99 VERIFY(!ret);
100 VERIFY(ssource.stop_requested());
101 VERIFY(!t1done.load());
102 VERIFY(!origsource.stop_requested());
103
104 std::this_thread::sleep_for(470ms);
105 origsource.request_stop();
106 }
107 VERIFY(origsource.stop_requested());
108 VERIFY(ssource.stop_requested());
109 }
110
111 //------------------------------------------------------
112
test_join()113 void test_join()
114 {
115 std::stop_source ssource;
116 VERIFY(ssource.stop_possible());
117 {
118 std::jthread t1([](std::stop_token stoken) {
119 for (int i=0; !stoken.stop_requested(); ++i) {
120 std::this_thread::sleep_for(100ms);
121 }
122 });
123 ssource = t1.get_stop_source();
124 std::jthread t2([ssource] () mutable {
125 for (int i=0; i < 10; ++i) {
126 std::this_thread::sleep_for(70ms);
127 }
128 ssource.request_stop();
129 });
130 // wait for all thread to finish:
131 t2.join();
132 VERIFY(!t2.joinable());
133 VERIFY(t1.joinable());
134 t1.join();
135 VERIFY(!t1.joinable());
136 }
137 }
138
139 //------------------------------------------------------
140
test_detach()141 void test_detach()
142 {
143 std::stop_source ssource;
144 VERIFY(ssource.stop_possible());
145 std::atomic<bool> t1FinallyInterrupted{false};
146 {
147 std::jthread t0;
148 std::jthread::id t1ID{std::this_thread::get_id()};
149 bool t1IsInterrupted;
150 std::stop_token t1InterruptToken;
151 std::atomic<bool> t1AllSet{false};
152 std::jthread t1([&t1ID, &t1IsInterrupted, &t1InterruptToken, &t1AllSet, &t1FinallyInterrupted]
153 (std::stop_token stoken) {
154 // check some values of the started thread:
155 t1ID = std::this_thread::get_id();
156 t1InterruptToken = stoken;
157 t1IsInterrupted = stoken.stop_requested();
158 VERIFY(stoken.stop_possible());
159 VERIFY(!stoken.stop_requested());
160 t1AllSet.store(true);
161 for (int i=0; !stoken.stop_requested(); ++i) {
162 std::this_thread::sleep_for(100ms);
163 }
164 t1FinallyInterrupted.store(true);
165 });
166 for (int i=0; !t1AllSet.load(); ++i) {
167 std::this_thread::sleep_for(10ms);
168 }
169 VERIFY(!t0.joinable());
170 VERIFY(t1.joinable());
171 VERIFY(t1ID == t1.get_id());
172 VERIFY(t1IsInterrupted == false);
173 VERIFY(t1InterruptToken == t1.get_stop_source().get_token());
174 ssource = t1.get_stop_source();
175 VERIFY(t1InterruptToken.stop_possible());
176 VERIFY(!t1InterruptToken.stop_requested());
177 t1.detach();
178 VERIFY(!t1.joinable());
179 }
180
181 VERIFY(!t1FinallyInterrupted.load());
182 ssource.request_stop();
183 VERIFY(ssource.stop_requested());
184 for (int i=0; !t1FinallyInterrupted.load() && i < 100; ++i) {
185 std::this_thread::sleep_for(100ms);
186 }
187 VERIFY(t1FinallyInterrupted.load());
188 }
189
190 //------------------------------------------------------
191
test_move_assignment()192 void test_move_assignment()
193 {
194 std::jthread thread1([]{});
195 std::jthread thread2([]{});
196
197 const auto id2 = thread2.get_id();
198 const auto ssource2 = thread2.get_stop_source();
199
200 thread1 = std::move(thread2);
201
202 VERIFY(thread1.get_id() == id2);
203 VERIFY(thread2.get_id() == std::jthread::id());
204
205 VERIFY(thread1.get_stop_source() == ssource2);
206 VERIFY(!thread2.get_stop_source().stop_possible());
207 }
208
main()209 int main()
210 {
211 std::set_terminate([](){
212 VERIFY(false);
213 });
214
215 test_no_stop_token();
216 test_stop_token();
217 test_join();
218 test_detach();
219 test_move_assignment();
220 }
221