From: Thomas Rodgers Date: Fri, 15 Nov 2019 03:09:19 +0000 (+0000) Subject: Support for jthread and stop_token X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=942c4b32b0553378f843e606bb56c417acbdc4be;p=gcc.git Support for jthread and stop_token * include/Makefile.am: Add header. * include/Makefile.in: Regenerate. * include/std/condition_variable: Add overloads for stop_token support to condition_variable_any. * include/std/stop_token: New file. * include/std/thread: Add jthread type. * include/std/version (__cpp_lib_jthread): New value. * testsuite/30_threads/condition_variable_any/stop_token/1.cc: New test. * testsuite/30_threads/condition_variable_any/stop_token/2.cc: New test. * testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc: New test. * testsuite/30_threads/jthread/1.cc: New test. * testsuite/30_threads/jthread/2.cc: New test. * testsuite/30_threads/jthread/jthread.cc: New test. * testsuite/30_threads/stop_token/1.cc: New test. * testsuite/30_threads/stop_token/2.cc: New test. * testsuite/30_threads/stop_token/stop_token.cc: New test. From-SVN: r278274 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 13cca24e368..973dec2daba 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,22 @@ +2019-11-14 Thomas Rodgers + + * include/Makefile.am: Add header. + * include/Makefile.in: Regenerate. + * include/std/condition_variable: Add overloads for stop_token support + to condition_variable_any. + * include/std/stop_token: New file. + * include/std/thread: Add jthread type. + * include/std/version (__cpp_lib_jthread): New value. + * testsuite/30_threads/condition_variable_any/stop_token/1.cc: New test. + * testsuite/30_threads/condition_variable_any/stop_token/2.cc: New test. + * testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc: New test. + * testsuite/30_threads/jthread/1.cc: New test. + * testsuite/30_threads/jthread/2.cc: New test. + * testsuite/30_threads/jthread/jthread.cc: New test. + * testsuite/30_threads/stop_token/1.cc: New test. + * testsuite/30_threads/stop_token/2.cc: New test. + * testsuite/30_threads/stop_token/stop_token.cc: New test. + 2019-11-14 Edward Smith-Rowland <3dw4rd@verizon.net> Implement the part of C++20 p1032 Misc constexpr bits. diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 49fd41360bc..6300de9e96d 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -74,6 +74,7 @@ std_headers = \ ${std_srcdir}/sstream \ ${std_srcdir}/stack \ ${std_srcdir}/stdexcept \ + ${std_srcdir}/stop_token \ ${std_srcdir}/streambuf \ ${std_srcdir}/string \ ${std_srcdir}/string_view \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index acc4fe5f4c9..ae4a493ea65 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -418,6 +418,7 @@ std_headers = \ ${std_srcdir}/sstream \ ${std_srcdir}/stack \ ${std_srcdir}/stdexcept \ + ${std_srcdir}/stop_token \ ${std_srcdir}/streambuf \ ${std_srcdir}/string \ ${std_srcdir}/string_view \ diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index cc96661e94c..8887cee29fa 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -36,6 +36,7 @@ #else #include + #include #include #include @@ -45,6 +46,11 @@ #include #include +#if __cplusplus > 201703L +#define __cpp_lib_jthread 201907L +#include +#endif + #if defined(_GLIBCXX_HAS_GTHREADS) namespace std _GLIBCXX_VISIBILITY(default) @@ -360,6 +366,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __rtime, _Predicate __p) { return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); } + +#ifdef __cpp_lib_jthread + template + bool wait_on(_Lock& __lock, + stop_token __stoken, + _Predicate __p) + { + if (__stoken.stop_requested()) + { + return __p(); + } + + std::stop_callback __cb(__stoken, [this] { notify_all(); }); + shared_ptr __mutex = _M_mutex; + while (!__p()) + { + unique_lock __my_lock(*__mutex); + if (__stoken.stop_requested()) + { + return false; + } + // *__mutex must be unlocked before re-locking __lock so move + // ownership of *__mutex lock to an object with shorter lifetime. + _Unlock<_Lock> __unlock(__lock); + unique_lock __my_lock2(std::move(__my_lock)); + _M_cond.wait(__my_lock2); + } + return true; + } + + template + bool wait_on_until(_Lock& __lock, + stop_token __stoken, + const chrono::time_point<_Clock, _Duration>& __abs_time, + _Predicate __p) + { + if (__stoken.stop_requested()) + { + return __p(); + } + + std::stop_callback __cb(__stoken, [this] { notify_all(); }); + shared_ptr __mutex = _M_mutex; + while (!__p()) + { + bool __stop; + { + unique_lock __my_lock(*__mutex); + if (__stoken.stop_requested()) + { + return false; + } + _Unlock<_Lock> __u(__lock); + unique_lock __my_lock2(std::move(__my_lock)); + const auto __status = _M_cond.wait_until(__my_lock2, __abs_time); + __stop = (__status == std::cv_status::timeout) || __stoken.stop_requested(); + } + if (__stop) + { + return __p(); + } + } + return true; + } + + template + bool wait_on_for(_Lock& __lock, + stop_token __stoken, + const chrono::duration<_Rep, _Period>& __rel_time, + _Predicate __p) + { + auto __abst = std::chrono::steady_clock::now() + __rel_time; + return wait_on_until(__lock, + std::move(__stoken), + __abst, + std::move(__p)); + } +#endif }; } // end inline namespace diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token new file mode 100644 index 00000000000..af64018bb0e --- /dev/null +++ b/libstdc++-v3/include/std/stop_token @@ -0,0 +1,370 @@ +// -*- C++ -*- + +// Copyright (C) 2019 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file include/stop_token + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_STOP_TOKEN +#define _GLIBCXX_STOP_TOKEN + +#if __cplusplus > 201703L + +#include +#include +#include +#include + +#define __cpp_lib_jthread 201907L + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class stop_source; + template + class stop_callback; + + struct nostopstate_t { explicit nostopstate_t() = default; }; + inline constexpr nostopstate_t nostopstate(); + + class stop_token + { + public: + stop_token() noexcept = default; + + stop_token(const stop_token& __other) noexcept = default; + stop_token(stop_token&& __other) noexcept = default; + + ~stop_token() = default; + + stop_token& + operator=(const stop_token& __rhs) noexcept = default; + + stop_token& + operator=(stop_token&& __rhs) noexcept; + + [[nodiscard]] + bool + stop_possible() const noexcept + { + return static_cast(_M_state); + } + + [[nodiscard]] + bool + stop_requested() const noexcept + { + return stop_possible() && _M_state->_M_stop_requested(); + } + + [[nodiscard]] + friend bool + operator==(const stop_token& __a, const stop_token& __b) + { + return __a._M_state == __b._M_state; + } + + [[nodiscard]] + friend bool + operator!=(const stop_token& __a, const stop_token& __b) + { + return __a._M_state == __b._M_state; + } + + private: + friend stop_source; + template + friend class stop_callback; + + struct _Stop_cb { + void(*_M_callback)(_Stop_cb*); + _Stop_cb* _M_prev = nullptr; + _Stop_cb* _M_next = nullptr; + + template + _Stop_cb(_Cb&& __cb) + : _M_callback(std::move(__cb)) + { } + + bool + _M_linked() const + { + return (_M_prev != nullptr) + || (_M_next != nullptr); + } + + static void + _S_execute(_Stop_cb* __cb) noexcept + { + __cb->_M_callback(__cb); + __cb->_M_prev = __cb->_M_next = nullptr; + } + }; + + struct _Stop_state_t { + std::atomic _M_stopped; + std::mutex _M_mtx; + _Stop_cb* _M_head = nullptr; + + _Stop_state_t() + : _M_stopped{false} + { } + + bool + _M_stop_requested() + { + return _M_stopped; + } + + bool + _M_request_stop() + { + bool __stopped = false; + if (_M_stopped.compare_exchange_strong(__stopped, true)) + { + std::unique_lock __lck{_M_mtx}; + while (_M_head) + { + auto __p = _M_head; + _M_head = _M_head->_M_next; + _Stop_cb::_S_execute(__p); + } + return true; + } + return false; + } + + bool + _M_register_callback(_Stop_cb* __cb) + { + std::unique_lock __lck{_M_mtx}; + if (_M_stopped) + return false; + + __cb->_M_next = _M_head; + if (_M_head) + { + _M_head->_M_prev = __cb; + } + _M_head = __cb; + return true; + } + + void + _M_remove_callback(_Stop_cb* __cb) + { + std::unique_lock __lck{_M_mtx}; + if (__cb == _M_head) + { + _M_head = _M_head->_M_next; + if (_M_head) + { + _M_head->_M_prev = nullptr; + } + } + else if (!__cb->_M_linked()) + { + return; + } + else + { + __cb->_M_prev->_M_next = __cb->_M_next; + if (__cb->_M_next) + { + __cb->_M_next->_M_prev = __cb->_M_prev; + } + } + } + }; + + using _Stop_state = std::shared_ptr<_Stop_state_t>; + _Stop_state _M_state; + + explicit stop_token(_Stop_state __state) + : _M_state{std::move(__state)} + { } + }; + + class stop_source { + using _Stop_state_t = stop_token::_Stop_state_t; + using _Stop_state = stop_token::_Stop_state; + + public: + stop_source() + : _M_state(std::make_shared<_Stop_state_t>()) + { } + + explicit stop_source(std::nostopstate_t) noexcept + { } + + stop_source(const stop_source& __other) noexcept + : _M_state(__other._M_state) + { } + + stop_source(stop_source&& __other) noexcept + : _M_state(std::move(__other._M_state)) + { } + + stop_source& + operator=(const stop_source& __rhs) noexcept + { + if (_M_state != __rhs._M_state) + _M_state = __rhs._M_state; + return *this; + } + + stop_source& + operator=(stop_source&& __rhs) noexcept + { + std::swap(_M_state, __rhs._M_state); + return *this; + } + + [[nodiscard]] + bool + stop_possible() const noexcept + { + return static_cast(_M_state); + } + + [[nodiscard]] + bool + stop_requested() const noexcept + { + return stop_possible() && _M_state->_M_stop_requested(); + } + + bool + request_stop() const noexcept + { + if (stop_possible()) + return _M_state->_M_request_stop(); + return false; + } + + [[nodiscard]] + stop_token + get_token() const noexcept + { + return stop_token{_M_state}; + } + + void + swap(stop_source& __other) noexcept + { + std::swap(_M_state, __other._M_state); + } + + [[nodiscard]] + friend bool + operator==(const stop_source& __a, const stop_source& __b) noexcept + { + return __a._M_state == __b._M_state; + } + + [[nodiscard]] + friend bool + operator!=(const stop_source& __a, const stop_source& __b) noexcept + { + return __a._M_state != __b._M_state; + } + + private: + _Stop_state _M_state; + }; + + template + class [[nodiscard]] stop_callback + : private stop_token::_Stop_cb + { + using _Stop_cb = stop_token::_Stop_cb; + using _Stop_state = stop_token::_Stop_state; + public: + using callback_type = _Callback; + + template, int> = 0> + explicit stop_callback(const stop_token& __token, _Cb&& __cb) + noexcept(std::is_nothrow_constructible_v<_Callback, _Cb>) + : _Stop_cb([](_Stop_cb* __that) noexcept + { + static_cast(__that)->_M_execute(); + }), + _M_cb(std::move(__cb)) + { + auto res = __token._M_state->_M_register_callback(this); + if (__token._M_state && res) + { + _M_state = __token._M_state; + } + } + + template, int> = 0> + explicit stop_callback(stop_token&& __token, _Cb&& __cb) + noexcept(std::is_nothrow_constructible_v<_Callback, _Cb>) + : _Stop_cb([](_Stop_cb* __that) noexcept + { + static_cast(__that)->_M_execute(); + }), + _M_cb(std::move(__cb)) + { + if (__token._M_state && __token._M_state->_M_register_callback(this)) + { + std::swap(_M_state, __token._M_state); + } + } + + ~stop_callback() + { + if (_M_state) + { + _M_state->_M_remove_callback(this); + } + } + + stop_callback(const stop_callback&) = delete; + stop_callback& operator=(const stop_callback&) = delete; + stop_callback(stop_callback&&) = delete; + stop_callback& operator=(stop_callback&&) = delete; + + private: + _Callback _M_cb; + _Stop_state _M_state = nullptr; + + void + _M_execute() noexcept + { + _M_cb(); + } + }; + + template + stop_callback(stop_token, _Callback) -> stop_callback<_Callback>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus >= 201703L +#endif // _GLIBCXX_STOP_TOKEN diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index 90b4be6cd16..93afa766d18 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -39,6 +39,13 @@ #include #include #include + +#if __cplusplus > 201703L +#define __cpp_lib_jthread 201907L +#include +#include +#endif + #include #include #include @@ -409,6 +416,124 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // @} group threads +#ifdef __cpp_lib_jthread + + class jthread + { + public: + using id = std::thread::id; + using native_handle_type = std::thread::native_handle_type; + + jthread() noexcept + : _M_stop_source{ nostopstate_t{ } } + { } + + template, jthread>>> + explicit + jthread(_Callable&& __f, _Args&&... __args) + : _M_thread{[](stop_token __token, auto&& __cb, auto&&... __args) + { + if constexpr(std::is_invocable_v<_Callable, stop_token, _Args...>) + { + std::invoke(std::forward(__cb), + std::move(__token), + std::forward(__args)...); + } + else + { + std::invoke(std::forward(__cb), + std::forward(__args)...); + } + }, + _M_stop_source.get_token(), + std::forward<_Callable>(__f), + std::forward<_Args>(__args)...} + { } + + jthread(const jthread&) = delete; + jthread(jthread&&) noexcept = default; + + ~jthread() + { + if (joinable()) + { + request_stop(); + join(); + } + } + + jthread& + operator=(const jthread&) = delete; + + jthread& + operator=(jthread&&) noexcept = default; + + void + swap(jthread& __other) noexcept + { + std::swap(_M_stop_source, __other._M_stop_source); + std::swap(_M_thread, __other._M_thread); + } + + bool + joinable() const noexcept + { + return _M_thread.joinable(); + } + + void + join() + { + _M_thread.join(); + } + + void + detach() + { + _M_thread.detach(); + } + + id + get_id() const noexcept + { + _M_thread.get_id(); + } + + native_handle_type + native_handle() + { + return _M_thread.native_handle(); + } + + static unsigned + hardware_concurrency() noexcept + { + return std::thread::hardware_concurrency(); + } + + [[nodiscard]] stop_source + get_stop_source() noexcept + { + return _M_stop_source; + } + + [[nodiscard]] stop_token + get_stop_token() const noexcept + { + return _M_stop_source.get_token(); + } + + bool request_stop() noexcept + { + return get_stop_source().request_stop(); + } + + private: + stop_source _M_stop_source; + std::thread _M_thread; + }; +#endif // __cpp_lib_jthread _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index fa6d27467f7..27807428903 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -187,6 +187,7 @@ #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_span 201902L +#define __cpp_lib_jthread 201907L #if __cpp_impl_three_way_comparison >= 201907L # define __cpp_lib_three_way_comparison 201711L #endif diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc new file mode 100644 index 00000000000..c1f4b5849ac --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_jthread +# error "Feature-test macro for jthread missing in " +#elif __cpp_lib_jthread != 201907L +# error "Feature-test macro for jthread has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc new file mode 100644 index 00000000000..621965c8910 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_jthread +# error "Feature-test macro for jthread missing in " +#elif __cpp_lib_jthread != 201907L +# error "Feature-test macro for jthread has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc new file mode 100644 index 00000000000..212fc949b3f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc @@ -0,0 +1,136 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run } +// { dg-require-effective-target c++2a } +// { dg-require-effective-target pthread } + +#include +#include +#include +#include +#include + +using namespace::std::literals; + +void test_wait_on_stop() +{ + bool ready = false; + std::mutex mtx; + std::condition_variable_any cv; + + std::stop_source src; + + auto tok = src.get_token(); + std::thread t([&ready, &mtx, &cv, tok] + { + std::unique_lock lck(mtx); + auto res = cv.wait_on(lck, tok, [&ready] { return ready; }); + if (!res) + { + VERIFY(tok.stop_requested()); + } + }); + + std::this_thread::sleep_for(0.5s); + VERIFY(!src.stop_requested()); + src.request_stop(); + t.join(); + VERIFY(src.stop_requested()); +} + +void test_wait_on_until(bool ck = true) +{ + bool ready = false; + std::mutex mtx; + std::condition_variable_any cv; + + std::stop_source src; + + auto abst = std::chrono::steady_clock::now() + 1.0s; + auto tok = src.get_token(); + std::thread t([ck, &ready, &mtx, &cv, abst, tok] + { + std::unique_lock lck(mtx); + auto res = cv.wait_on_until(lck, tok, abst, [&ready] { return ready; }); + if (!res && ck) + { + VERIFY(tok.stop_requested()); + } + }); + + if (ck) + { + std::this_thread::sleep_for(0.5s); + VERIFY(!src.stop_requested()); + src.request_stop(); + t.join(); + VERIFY(src.stop_requested()); + } + else + { + std::this_thread::sleep_for(1.5s); + t.join(); + VERIFY(!src.stop_requested()); + } +} + +void test_wait_on_for(bool ck = true) +{ + bool ready = false; + std::mutex mtx; + std::condition_variable_any cv; + + std::stop_source src; + + auto tok = src.get_token(); + std::thread t([ck, &ready, &mtx, &cv, tok] + { + std::unique_lock lck(mtx); + auto res = cv.wait_on_for(lck, tok, 1.0s, [&ready] { return ready; }); + if (!res && ck) + { + VERIFY(tok.stop_requested()); + } + }); + + if (ck) + { + std::this_thread::sleep_for(0.5s); + VERIFY(!src.stop_requested()); + src.request_stop(); + t.join(); + VERIFY(src.stop_requested()); + } + else + { + std::this_thread::sleep_for(1.5s); + t.join(); + VERIFY(!src.stop_requested()); + }; +} + +int main() +{ + test_wait_on_stop(); + test_wait_on_until(false); + test_wait_on_until(); + test_wait_on_for(); + test_wait_on_for(false); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/jthread/1.cc b/libstdc++-v3/testsuite/30_threads/jthread/1.cc new file mode 100644 index 00000000000..1fb5650dbc6 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/jthread/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_jthread +# error "Feature-test macro for jthread missing in " +#elif __cpp_lib_jthread != 201907L +# error "Feature-test macro for jthread has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/jthread/2.cc b/libstdc++-v3/testsuite/30_threads/jthread/2.cc new file mode 100644 index 00000000000..621965c8910 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/jthread/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_jthread +# error "Feature-test macro for jthread missing in " +#elif __cpp_lib_jthread != 201907L +# error "Feature-test macro for jthread has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc b/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc new file mode 100644 index 00000000000..c29db212167 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc @@ -0,0 +1,198 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include +#include +#include +#include + +using namespace::std::literals; + +//------------------------------------------------------ + +void test_no_stop_token() +{ + // test the basic jthread API (not taking stop_token arg) + + assert(std::jthread::hardware_concurrency() == std::thread::hardware_concurrency()); + std::stop_token stoken; + assert(!stoken.stop_possible()); + { + std::jthread::id t1ID{std::this_thread::get_id()}; + std::atomic 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); + } + assert(t1.joinable()); + assert(t1ID == t1.get_id()); + stoken = t1.get_stop_token(); + assert(!stoken.stop_requested()); + } + assert(stoken.stop_requested()); +} + +//------------------------------------------------------ + +void test_stop_token() +{ + // test the basic thread API (taking stop_token arg) + + std::stop_source ssource; + std::stop_source origsource; + assert(ssource.stop_possible()); + assert(!ssource.stop_requested()); + { + std::jthread::id t1ID{std::this_thread::get_id()}; + std::atomic t1AllSet{false}; + std::atomic 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: + assert(t1.joinable()); + assert(t1ID == t1.get_id()); + + std::this_thread::sleep_for(470ms); + origsource = std::move(ssource); + ssource = t1.get_stop_source(); + assert(!ssource.stop_requested()); + auto ret = ssource.request_stop(); + assert(ret); + ret = ssource.request_stop(); + assert(!ret); + assert(ssource.stop_requested()); + assert(!t1done.load()); + assert(!origsource.stop_requested()); + + std::this_thread::sleep_for(470ms); + origsource.request_stop(); + } + assert(origsource.stop_requested()); + assert(ssource.stop_requested()); +} + +//------------------------------------------------------ + +void test_join() +{ + std::stop_source ssource; + assert(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(); + assert(!t2.joinable()); + assert(t1.joinable()); + t1.join(); + assert(!t1.joinable()); + } +} + +//------------------------------------------------------ + +void test_detach() +{ + std::stop_source ssource; + assert(ssource.stop_possible()); + std::atomic t1FinallyInterrupted{false}; + { + std::jthread t0; + std::jthread::id t1ID{std::this_thread::get_id()}; + bool t1IsInterrupted; + std::stop_token t1InterruptToken; + std::atomic 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(); + assert(stoken.stop_possible()); + assert(!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); + } + assert(!t0.joinable()); + assert(t1.joinable()); + assert(t1ID == t1.get_id()); + assert(t1IsInterrupted == false); + assert(t1InterruptToken == t1.get_stop_source().get_token()); + ssource = t1.get_stop_source(); + assert(t1InterruptToken.stop_possible()); + assert(!t1InterruptToken.stop_requested()); + t1.detach(); + assert(!t1.joinable()); + } + + assert(!t1FinallyInterrupted.load()); + ssource.request_stop(); + assert(ssource.stop_requested()); + for (int i=0; !t1FinallyInterrupted.load() && i < 100; ++i) { + std::this_thread::sleep_for(100ms); + } + assert(t1FinallyInterrupted.load()); +} + +int main() +{ + std::set_terminate([](){ + assert(false); + }); + + test_no_stop_token(); + test_stop_token(); + test_join(); + test_detach(); +} + diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/1.cc b/libstdc++-v3/testsuite/30_threads/stop_token/1.cc new file mode 100644 index 00000000000..4c0e7a33d25 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/stop_token/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_jthread +# error "Feature-test macro for jthread missing in " +#elif __cpp_lib_jthread != 201907L +# error "Feature-test macro for jthread has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/2.cc b/libstdc++-v3/testsuite/30_threads/stop_token/2.cc new file mode 100644 index 00000000000..621965c8910 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/stop_token/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_jthread +# error "Feature-test macro for jthread missing in " +#elif __cpp_lib_jthread != 201907L +# error "Feature-test macro for jthread has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc new file mode 100644 index 00000000000..37d79e8a64e --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc @@ -0,0 +1,100 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run } +// { dg-require-effective-target c++2a } +// { dg-require-effective-target pthread } + +#include +#include +#include + +int main() +{ + // create stop_source + std::stop_source ssrc; + VERIFY(ssrc.stop_possible()); + VERIFY(!ssrc.stop_requested()); + + // create stop_token from stop_source + std::stop_token stok{ssrc.get_token()}; + VERIFY(ssrc.stop_possible()); + VERIFY(!ssrc.stop_requested()); + VERIFY(stok.stop_possible()); + VERIFY(!stok.stop_requested()); + + // register callback + bool cb1called{false}; + auto cb1 = [&]{ + std::cout << "cb1" << std::endl; + cb1called = true; + }; + { + std::stop_callback scb1{stok, cb1}; + VERIFY(ssrc.stop_possible()); + VERIFY(!ssrc.stop_requested()); + VERIFY(stok.stop_possible()); + VERIFY(!stok.stop_requested()); + VERIFY(!cb1called); + } // unregister callback + + // register another callback + bool cb2called{false}; + auto cb2 = [&]{ + VERIFY(stok.stop_requested()); + cb2called = true; + }; + std::stop_callback scb2a{stok, cb2}; // copies cb2 + // std::stop_callback scb2b{stok, std::move(cb2)}; + VERIFY(ssrc.stop_possible()); + VERIFY(!ssrc.stop_requested()); + VERIFY(stok.stop_possible()); + VERIFY(!stok.stop_requested()); + VERIFY(!cb1called); + VERIFY(!cb2called); + + // request stop + auto b = ssrc.request_stop(); + VERIFY(b); + VERIFY(ssrc.stop_possible()); + VERIFY(ssrc.stop_requested()); + VERIFY(stok.stop_possible()); + VERIFY(stok.stop_requested()); + VERIFY(!cb1called); + VERIFY(cb2called); + + b = ssrc.request_stop(); + VERIFY(!b); + + // TODO verify the standard requires this +#if 0 + // register another callback + bool cb3called{false}; + std::stop_callback scb3{stok, [&] + { + cb3called = true; + }}; + VERIFY(ssrc.stop_possible()); + VERIFY(ssrc.stop_requested()); + VERIFY(stok.stop_possible()); + VERIFY(stok.stop_requested()); + VERIFY(!cb1called); + VERIFY(cb2called); + VERIFY(cb3called); +#endif +}