// -*- C++ -*- // Copyright (C) 2019-2020 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 #include #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201907L #endif namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Tag type indicating a stop_source should have no shared-stop-state. struct nostopstate_t { explicit nostopstate_t() = default; }; inline constexpr nostopstate_t nostopstate{}; /// Allow testing whether a stop request has been made on a `stop_source`. 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 = default; [[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(); } void swap(stop_token& __rhs) noexcept { _M_state.swap(__rhs._M_state); } [[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; } friend void swap(stop_token& __lhs, stop_token& __rhs) noexcept { __lhs.swap(__rhs); } private: friend class 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::forward<_Cb>(__cb)) { } bool _M_linked() const noexcept { 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{false}; _Stop_cb* _M_head = nullptr; #ifdef _GLIBCXX_HAS_GTHREADS std::mutex _M_mtx; #endif _Stop_state_t() = default; bool _M_stop_requested() noexcept { return _M_stopped; } bool _M_request_stop() { bool __stopped = false; if (_M_stopped.compare_exchange_strong(__stopped, true)) { #ifdef _GLIBCXX_HAS_GTHREADS std::lock_guard __lck{_M_mtx}; #endif 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) { #ifdef _GLIBCXX_HAS_GTHREADS std::lock_guard __lck{_M_mtx}; #endif 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) { #ifdef _GLIBCXX_HAS_GTHREADS std::lock_guard __lck{_M_mtx}; #endif 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(const _Stop_state& __state) noexcept : _M_state{__state} { } }; /// A type that allows a stop request to be made. class stop_source { public: stop_source() : _M_state(std::make_shared()) { } 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 { _M_state.swap(__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; } friend void swap(stop_source& __lhs, stop_source& __rhs) noexcept { __lhs.swap(__rhs); } private: stop_token::_Stop_state _M_state; }; /// A wrapper for callbacks to be run when a stop request is made. template class [[nodiscard]] stop_callback : private stop_token::_Stop_cb { public: using callback_type = _Callback; template, int> = 0> explicit stop_callback(const stop_token& __token, _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>) : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb)) { if (auto __state = __token._M_state) { if (__state->_M_stop_requested()) _S_execute(this); // ensures std::terminate on throw else if (__state->_M_register_callback(this)) _M_state.swap(__state); } } template, int> = 0> explicit stop_callback(stop_token&& __token, _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>) : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb)) { if (auto& __state = __token._M_state) { if (__state->_M_stop_requested()) _S_execute(this); // ensures std::terminate on throw else if (__state->_M_register_callback(this)) _M_state.swap(__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_token::_Stop_state _M_state = nullptr; static void _S_execute(_Stop_cb* __that) noexcept { static_cast(__that)->_M_cb(); } }; template stop_callback(stop_token, _Callback) -> stop_callback<_Callback>; _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif // __cplusplus > 201703L #endif // _GLIBCXX_STOP_TOKEN