};
ptrdiff_t _M_expected;
- unique_ptr<__state_t[]> _M_state;
- __atomic_base<ptrdiff_t> _M_expected_adjustment;
- _CompletionF _M_completion;
+ __atomic_base<__state_t*> _M_state{nullptr};
+ __atomic_base<ptrdiff_t> _M_expected_adjustment{0};
+ [[no_unique_address]] _CompletionF _M_completion;
- alignas(__phase_alignment) __barrier_phase_t _M_phase;
+ alignas(__phase_alignment) __barrier_phase_t _M_phase{};
bool
_M_arrive(__barrier_phase_t __old_phase, size_t __current)
size_t __current_expected = _M_expected;
__current %= ((_M_expected + 1) >> 1);
+ __state_t* const __state = _M_state.load(memory_order_relaxed);
+
for (int __round = 0; ; ++__round)
{
if (__current_expected <= 1)
if (__current == __end_node)
__current = 0;
auto __expect = __old_phase;
- __atomic_phase_ref_t __phase(_M_state[__current]
+ __atomic_phase_ref_t __phase(__state[__current]
.__tickets[__round]);
if (__current == __last_node && (__current_expected & 1))
{
}
}
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3898. Possibly unintended preconditions for completion functions
+ void _M_invoke_completion() noexcept { _M_completion(); }
+
public:
using arrival_token = __barrier_phase_t;
static constexpr ptrdiff_t
max() noexcept
- { return __PTRDIFF_MAX__; }
+ { return __PTRDIFF_MAX__ - 1; }
+ constexpr
__tree_barrier(ptrdiff_t __expected, _CompletionF __completion)
- : _M_expected(__expected), _M_expected_adjustment(0),
- _M_completion(move(__completion)),
- _M_phase(static_cast<__barrier_phase_t>(0))
+ : _M_expected(__expected), _M_completion(std::move(__completion))
{
- size_t const __count = (_M_expected + 1) >> 1;
+ __glibcxx_assert(__expected >= 0 && __expected <= max());
- _M_state = std::make_unique<__state_t[]>(__count);
+ if (!std::is_constant_evaluated())
+ {
+ size_t const __count = (_M_expected + 1) >> 1;
+ _M_state.store(new __state_t[__count], memory_order_release);
+ }
}
[[nodiscard]] arrival_token
arrive(ptrdiff_t __update)
{
+ __glibcxx_assert(__update > 0);
+ // FIXME: Check that update is less than or equal to the expected count
+ // for the current barrier phase.
+
std::hash<std::thread::id> __hasher;
size_t __current = __hasher(std::this_thread::get_id());
__atomic_phase_ref_t __phase(_M_phase);
const auto __old_phase = __phase.load(memory_order_relaxed);
const auto __cur = static_cast<unsigned char>(__old_phase);
- for(; __update; --__update)
+
+ if (__cur == 0 && !_M_state.load(memory_order_relaxed)) [[unlikely]]
{
- if(_M_arrive(__old_phase, __current))
+ size_t const __count = (_M_expected + 1) >> 1;
+ auto __p = make_unique<__state_t[]>(__count);
+ __state_t* __val = nullptr;
+ if (_M_state.compare_exchange_strong(__val, __p.get(),
+ memory_order_seq_cst,
+ memory_order_acquire))
+ __p.release();
+ }
+
+ for (; __update; --__update)
+ {
+ if (_M_arrive(__old_phase, __current))
{
- _M_completion();
+ _M_invoke_completion();
_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
_M_expected_adjustment.store(0, memory_order_relaxed);
auto __new_phase = static_cast<__barrier_phase_t>(__cur + 2);
template<typename _CompletionF = __empty_completion>
class barrier
{
+ static_assert(is_invocable_v<_CompletionF&>);
+
// Note, we may introduce a "central" barrier algorithm at some point
// for more space constrained targets
using __algorithm_t = __tree_barrier<_CompletionF>;
max() noexcept
{ return __algorithm_t::max(); }
- explicit
+ constexpr explicit
barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
: _M_b(__count, std::move(__completion))
{ }