gcc/libstdc++-v3/include/std/stop_token
Jonathan Wakely a4a1f96551 libstdc++: Remove redundant inequality operators in <stop_token>
* include/std/stop_token (stop_token): Remove operator!= (LWG 3254).
	(stop_source): Likewise (LWG 3362).
	* testsuite/30_threads/stop_token/stop_source.cc: Test equality
	comparisons.

From-SVN: r279897
2020-01-06 12:06:47 +00:00

376 lines
8.9 KiB
C++

// <stop_token> -*- 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
// <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 <atomic>
#include <bits/std_mutex.h>
#include <ext/concurrence.h>
#include <bits/unique_ptr.h>
#include <bits/shared_ptr.h>
#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<bool>(_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; }
friend void
swap(stop_token& __lhs, stop_token& __rhs) noexcept
{ __lhs.swap(__rhs); }
private:
friend class 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::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<bool> _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<std::mutex> __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<std::mutex> __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<std::mutex> __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<stop_token::_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
{
_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;
}
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<typename _Callback>
class [[nodiscard]] stop_callback
: private stop_token::_Stop_cb
{
public:
using callback_type = _Callback;
template<typename _Cb,
enable_if_t<is_constructible_v<_Callback, _Cb>, 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<typename _Cb,
enable_if_t<is_constructible_v<_Callback, _Cb>, 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<stop_callback*>(__that)->_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