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