| // Copyright (C) 2019-2021 Free Software Foundation, Inc. |
| // |
| // This file is part of the GNU ISO C++ Library. This library is free |
| // software; you can redistribute it and/or modify it under the |
| // terms of the GNU General Public License as published by the |
| // Free Software Foundation; either version 3, or (at your option) |
| // any later version. |
| |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| // You should have received a copy of the GNU General Public License along |
| // with this library; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| // { dg-options "-std=gnu++2a -pthread" } |
| // { dg-do run { target c++2a } } |
| // { dg-add-options libatomic } |
| // { dg-additional-options "-pthread" { target pthread } } |
| // { dg-require-gthreads "" } |
| |
| #include <thread> |
| #include <chrono> |
| #include <atomic> |
| #include <testsuite_hooks.h> |
| |
| using namespace std::literals; |
| |
| //------------------------------------------------------ |
| |
| void test_no_stop_token() |
| { |
| // test the basic jthread API (not taking stop_token arg) |
| |
| VERIFY(std::jthread::hardware_concurrency() == std::thread::hardware_concurrency()); |
| std::stop_token stoken; |
| VERIFY(!stoken.stop_possible()); |
| { |
| std::jthread::id t1ID{std::this_thread::get_id()}; |
| std::atomic<bool> t1AllSet{false}; |
| std::jthread t1([&t1ID, &t1AllSet] { |
| t1ID = std::this_thread::get_id(); |
| t1AllSet.store(true); |
| for (int c='9'; c>='0'; --c) { |
| std::this_thread::sleep_for(222ms); |
| } |
| }); |
| for (int i=0; !t1AllSet.load(); ++i) { |
| std::this_thread::sleep_for(10ms); |
| } |
| VERIFY(t1.joinable()); |
| VERIFY(t1ID == t1.get_id()); |
| stoken = t1.get_stop_token(); |
| VERIFY(!stoken.stop_requested()); |
| } |
| VERIFY(stoken.stop_requested()); |
| } |
| |
| //------------------------------------------------------ |
| |
| void test_stop_token() |
| { |
| // test the basic thread API (taking stop_token arg) |
| |
| std::stop_source ssource; |
| std::stop_source origsource; |
| VERIFY(ssource.stop_possible()); |
| VERIFY(!ssource.stop_requested()); |
| { |
| std::jthread::id t1ID{std::this_thread::get_id()}; |
| std::atomic<bool> t1AllSet{false}; |
| std::atomic<bool> t1done{false}; |
| std::jthread t1([&t1ID, &t1AllSet, &t1done] (std::stop_token st) { |
| // check some values of the started thread: |
| t1ID = std::this_thread::get_id(); |
| t1AllSet.store(true); |
| for (int i=0; !st.stop_requested(); ++i) { |
| std::this_thread::sleep_for(100ms); |
| } |
| t1done.store(true); |
| }, |
| ssource.get_token()); |
| for (int i=0; !t1AllSet.load(); ++i) { |
| std::this_thread::sleep_for(10ms); |
| } |
| // and check all values: |
| VERIFY(t1.joinable()); |
| VERIFY(t1ID == t1.get_id()); |
| |
| std::this_thread::sleep_for(470ms); |
| origsource = std::move(ssource); |
| ssource = t1.get_stop_source(); |
| VERIFY(!ssource.stop_requested()); |
| auto ret = ssource.request_stop(); |
| VERIFY(ret); |
| ret = ssource.request_stop(); |
| VERIFY(!ret); |
| VERIFY(ssource.stop_requested()); |
| VERIFY(!t1done.load()); |
| VERIFY(!origsource.stop_requested()); |
| |
| std::this_thread::sleep_for(470ms); |
| origsource.request_stop(); |
| } |
| VERIFY(origsource.stop_requested()); |
| VERIFY(ssource.stop_requested()); |
| } |
| |
| //------------------------------------------------------ |
| |
| void test_join() |
| { |
| std::stop_source ssource; |
| VERIFY(ssource.stop_possible()); |
| { |
| std::jthread t1([](std::stop_token stoken) { |
| for (int i=0; !stoken.stop_requested(); ++i) { |
| std::this_thread::sleep_for(100ms); |
| } |
| }); |
| ssource = t1.get_stop_source(); |
| std::jthread t2([ssource] () mutable { |
| for (int i=0; i < 10; ++i) { |
| std::this_thread::sleep_for(70ms); |
| } |
| ssource.request_stop(); |
| }); |
| // wait for all thread to finish: |
| t2.join(); |
| VERIFY(!t2.joinable()); |
| VERIFY(t1.joinable()); |
| t1.join(); |
| VERIFY(!t1.joinable()); |
| } |
| } |
| |
| //------------------------------------------------------ |
| |
| void test_detach() |
| { |
| std::stop_source ssource; |
| VERIFY(ssource.stop_possible()); |
| std::atomic<bool> t1FinallyInterrupted{false}; |
| { |
| std::jthread t0; |
| std::jthread::id t1ID{std::this_thread::get_id()}; |
| bool t1IsInterrupted; |
| std::stop_token t1InterruptToken; |
| std::atomic<bool> t1AllSet{false}; |
| std::jthread t1([&t1ID, &t1IsInterrupted, &t1InterruptToken, &t1AllSet, &t1FinallyInterrupted] |
| (std::stop_token stoken) { |
| // check some values of the started thread: |
| t1ID = std::this_thread::get_id(); |
| t1InterruptToken = stoken; |
| t1IsInterrupted = stoken.stop_requested(); |
| VERIFY(stoken.stop_possible()); |
| VERIFY(!stoken.stop_requested()); |
| t1AllSet.store(true); |
| for (int i=0; !stoken.stop_requested(); ++i) { |
| std::this_thread::sleep_for(100ms); |
| } |
| t1FinallyInterrupted.store(true); |
| }); |
| for (int i=0; !t1AllSet.load(); ++i) { |
| std::this_thread::sleep_for(10ms); |
| } |
| VERIFY(!t0.joinable()); |
| VERIFY(t1.joinable()); |
| VERIFY(t1ID == t1.get_id()); |
| VERIFY(t1IsInterrupted == false); |
| VERIFY(t1InterruptToken == t1.get_stop_source().get_token()); |
| ssource = t1.get_stop_source(); |
| VERIFY(t1InterruptToken.stop_possible()); |
| VERIFY(!t1InterruptToken.stop_requested()); |
| t1.detach(); |
| VERIFY(!t1.joinable()); |
| } |
| |
| VERIFY(!t1FinallyInterrupted.load()); |
| ssource.request_stop(); |
| VERIFY(ssource.stop_requested()); |
| for (int i=0; !t1FinallyInterrupted.load() && i < 100; ++i) { |
| std::this_thread::sleep_for(100ms); |
| } |
| VERIFY(t1FinallyInterrupted.load()); |
| } |
| |
| //------------------------------------------------------ |
| |
| void test_move_assignment() |
| { |
| std::jthread thread1([]{}); |
| std::jthread thread2([]{}); |
| |
| const auto id2 = thread2.get_id(); |
| const auto ssource2 = thread2.get_stop_source(); |
| |
| thread1 = std::move(thread2); |
| |
| VERIFY(thread1.get_id() == id2); |
| VERIFY(thread2.get_id() == std::jthread::id()); |
| |
| VERIFY(thread1.get_stop_source() == ssource2); |
| VERIFY(!thread2.get_stop_source().stop_possible()); |
| } |
| |
| int main() |
| { |
| std::set_terminate([](){ |
| VERIFY(false); |
| }); |
| |
| test_no_stop_token(); |
| test_stop_token(); |
| test_join(); |
| test_detach(); |
| test_move_assignment(); |
| } |