diff options
author | Thomas Rodgers <trodgers@redhat.com> | 2019-11-15 03:09:19 +0000 |
---|---|---|
committer | Thomas Rodgers <rodgertq@gcc.gnu.org> | 2019-11-15 03:09:19 +0000 |
commit | 942c4b32b0553378f843e606bb56c417acbdc4be (patch) | |
tree | 41fec04827f82dc4850fc993c2d0958d8a12fb02 /libstdc++-v3/include/std | |
parent | f8aea5e37d12e09fef68b854a190f71d6b39e11f (diff) | |
download | gcc-942c4b32b0553378f843e606bb56c417acbdc4be.tar.gz |
Support for jthread and stop_token
* include/Makefile.am: Add <stop_token> 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
Diffstat (limited to 'libstdc++-v3/include/std')
-rw-r--r-- | libstdc++-v3/include/std/condition_variable | 84 | ||||
-rw-r--r-- | libstdc++-v3/include/std/stop_token | 370 | ||||
-rw-r--r-- | libstdc++-v3/include/std/thread | 125 | ||||
-rw-r--r-- | libstdc++-v3/include/std/version | 1 |
4 files changed, 580 insertions, 0 deletions
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 <chrono> + #include <bits/std_mutex.h> #include <bits/unique_lock.h> #include <ext/concurrence.h> @@ -45,6 +46,11 @@ #include <bits/shared_ptr.h> #include <bits/cxxabi_forced.h> +#if __cplusplus > 201703L +#define __cpp_lib_jthread 201907L +#include <stop_token> +#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 <class _Lock, class _Predicate> + 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> __mutex = _M_mutex; + while (!__p()) + { + unique_lock<mutex> __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<mutex> __my_lock2(std::move(__my_lock)); + _M_cond.wait(__my_lock2); + } + return true; + } + + template <class _Lock, class _Clock, class _Duration, class _Predicate> + 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> __mutex = _M_mutex; + while (!__p()) + { + bool __stop; + { + unique_lock<mutex> __my_lock(*__mutex); + if (__stoken.stop_requested()) + { + return false; + } + _Unlock<_Lock> __u(__lock); + unique_lock<mutex> __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 <class _Lock, class _Rep, class _Period, class _Predicate> + 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 @@ +// <stop_token> -*- 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 +// <http://www.gnu.org/licenses/>. + +/** @file include/stop_token + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_STOP_TOKEN +#define _GLIBCXX_STOP_TOKEN + +#if __cplusplus > 201703L + +#include <type_traits> +#include <memory> +#include <mutex> +#include <atomic> + +#define __cpp_lib_jthread 201907L + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class stop_source; + template<typename _Callback> + 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<bool>(_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<typename _Callback> + friend class stop_callback; + + struct _Stop_cb { + void(*_M_callback)(_Stop_cb*); + _Stop_cb* _M_prev = nullptr; + _Stop_cb* _M_next = nullptr; + + template<typename _Cb> + _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<bool> _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<std::mutex> __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<std::mutex> __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<std::mutex> __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<bool>(_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<typename _Callback> + 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<typename _Cb, + std::enable_if_t<std::is_constructible_v<_Callback, _Cb>, 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<stop_callback*>(__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<typename _Cb, + std::enable_if_t<std::is_constructible_v<_Callback, _Cb>, 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<stop_callback*>(__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<typename _Callback> + 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 <memory> #include <tuple> #include <cerrno> + +#if __cplusplus > 201703L +#define __cpp_lib_jthread 201907L +#include <functional> +#include <stop_token> +#endif + #include <bits/functexcept.h> #include <bits/functional_hash.h> #include <bits/invoke.h> @@ -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<typename _Callable, typename... _Args, + typename = std::enable_if_t<!std::is_same_v<std::decay_t<_Callable>, 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<decltype(__cb)>(__cb), + std::move(__token), + std::forward<decltype(__args)>(__args)...); + } + else + { + std::invoke(std::forward<decltype(__cb)>(__cb), + std::forward<decltype(__args)>(__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 |