17#ifndef ION_BARRIER_HPP
18#define ION_BARRIER_HPP
25#include <ion/detail/barrier.hpp>
26#include <ion/detail/macros.hpp>
40 ArrivalToken(ArrivalToken&&)
noexcept =
default;
41 ArrivalToken& operator=(ArrivalToken&&)
noexcept =
default;
44 enum Expired : std::uint8_t {
51 inline explicit ArrivalToken(std::uint64_t last, Expired expired);
86template <
class CompletionFunction = detail::NoOp>
87class Barrier : detail::BarrierBase<CompletionFunction> {
89template <
class CompletionFunction = *unspecified*>
101 constexpr explicit Barrier(std::ptrdiff_t expected,
102 CompletionFunction func = CompletionFunction()) noexcept;
170 static constexpr auto
Max() noexcept -> std::ptrdiff_t;
173 ION_FORCE_INLINE inline
void Signal() noexcept;
179 std::atomic<std::ptrdiff_t> m_expected;
184 std::atomic<std::ptrdiff_t> m_remaining;
190 alignas(ION_DESTRUCTIVE_INTERFERENCE_SIZE) std::atomic<std::uint64_t> m_signal;
228template <
class CompletionFunction = detail::NoOp>
240 CompletionFunction func = CompletionFunction()) noexcept;
277 template <class WaitFunction = detail::NoOp>
279 std::uint32_t n = 1u,
280 WaitFunction&& wait = WaitFunction()) noexcept;
285 static constexpr auto
Max() noexcept -> std::uint32_t;
288 static ION_FORCE_INLINE constexpr auto
289 MakeRemaining(std::uint64_t expected) noexcept -> std::uint64_t;
290 template <class WaitFunction>
291 void Wait(std::uint64_t last, WaitFunction&& wait) const noexcept;
292 inline
void Signal(
bool invoke) noexcept;
293 template <class WaitFunction>
294 inline
void AwaitArrivals(WaitFunction&& wait) noexcept;
303 std::atomic<std::uint64_t> m_remaining;
309 std::uint32_t const m_expected;
315 alignas(ION_DESTRUCTIVE_INTERFERENCE_SIZE) std::atomic<std::uint64_t> m_signal;
318template <class CompletionFunction>
320 CompletionFunction func) noexcept
321 : detail::BarrierBase<CompletionFunction>(std::move(func))
322 , m_expected(expected)
323 , m_remaining(expected)
325 assert(expected >= 0 && expected <=
Max());
328template <
class CompletionFunction>
338 auto signal = m_signal.load(std::memory_order_relaxed);
342 auto remaining_pre_sub = m_remaining.fetch_sub(n, std::memory_order_release);
343 assert(remaining_pre_sub >= n);
344 if (remaining_pre_sub == n) {
350 std::atomic_thread_fence(std::memory_order_acquire);
351 ION_ANNOTATE_ACQUIRE(&m_remaining);
355 return ArrivalToken(signal, ArrivalToken::AlreadyExpired);
363template <
class CompletionFunction>
365 if (token.m_expired == ArrivalToken::AlreadyExpired) {
371 auto sig = m_signal.load(std::memory_order_relaxed);
372 if (sig != token.m_last) {
374 std::atomic_thread_fence(std::memory_order_acquire);
375 ION_ANNOTATE_ACQUIRE(
const_cast<void*
>(
static_cast<void const*
>(&m_signal)));
382template <
class CompletionFunction>
384 return std::numeric_limits<std::ptrdiff_t>::max();
387ArrivalToken::ArrivalToken(std::uint64_t last, Expired expired) : m_last(last), m_expired(expired) {
390template <
class CompletionFunction>
395template <
class CompletionFunction>
402 (void)m_expected.fetch_sub(1, std::memory_order_relaxed);
405 auto remaining_pre_sub = m_remaining.fetch_sub(1, std::memory_order_release);
406 assert(remaining_pre_sub >= 1);
407 if (remaining_pre_sub == 1) {
413 std::atomic_thread_fence(std::memory_order_acquire);
414 ION_ANNOTATE_ACQUIRE(&m_remaining);
419template <
class CompletionFunction>
420void Barrier<CompletionFunction>::Signal() noexcept {
422 if constexpr (!detail::IsNoOp<CompletionFunction>) {
428 constexpr auto acquire_order = []() -> std::memory_order {
429 if constexpr (!detail::IsNoOp<CompletionFunction>) {
431 return std::memory_order_relaxed;
433 return std::memory_order_consume;
438 m_remaining.store(m_expected.load(acquire_order), std::memory_order_relaxed);
441 (void)m_signal.fetch_add(1, std::memory_order_release);
444template <
class CompletionFunction>
446 CompletionFunction func) noexcept
447 : detail::BarrierBase<CompletionFunction>(std::move(func))
448 , m_remaining(MakeRemaining(expected))
449 , m_expected(expected)
451 assert(expected <=
Max());
454template <
class CompletionFunction>
456 return std::numeric_limits<std::uint32_t>::max();
459template <
class CompletionFunction>
461FlexBarrier<CompletionFunction>::MakeRemaining(std::uint64_t expected)
noexcept -> std::uint64_t {
462 return 0xffff'ffff'0000'0000 | expected;
465template <
class CompletionFunction>
466template <
class WaitFunction>
469 WaitFunction&& wait)
noexcept {
478 auto signal = m_signal.load(std::memory_order_relaxed);
481 ?
static_cast<std::uint64_t
>(n) + 0x1'0000'0000
482 :
static_cast<std::uint64_t
>(n);
485 auto const value_pre_sub = m_remaining.fetch_sub(value, std::memory_order_release);
486 auto const remaining = (value_pre_sub & 0xffff'ffff) - n;
487 auto const any_cf = value_pre_sub >> 32 != 0xffff'ffff;
490 if (remaining == 0u) {
504 std::atomic_thread_fence(std::memory_order_acquire);
505 ION_ANNOTATE_ACQUIRE(&m_remaining);
509 Wait(signal, std::forward<WaitFunction>(wait));
532 Wait(signal, std::forward<WaitFunction>(wait));
535 AwaitArrivals(std::forward<WaitFunction>(wait));
537 std::atomic_thread_fence(std::memory_order_acquire);
538 ION_ANNOTATE_ACQUIRE(&m_remaining);
544template <
class CompletionFunction>
545template <
class WaitFunction>
546void FlexBarrier<CompletionFunction>::Wait(std::uint64_t last,
547 WaitFunction&& wait)
const noexcept {
550 auto sig = m_signal.load(std::memory_order_relaxed);
551 if (ION_UNLIKELY(sig != last)) {
553 std::atomic_thread_fence(std::memory_order_acquire);
554 ION_ANNOTATE_ACQUIRE(
const_cast<void*
>(
static_cast<void const*
>(&m_signal)));
557 detail::Invoke(wait);
562template <
class CompletionFunction>
563void FlexBarrier<CompletionFunction>::Signal(
bool invoke)
noexcept {
565 if constexpr (!detail::IsNoOp<CompletionFunction>) {
574 m_remaining.store(MakeRemaining(m_expected), std::memory_order_relaxed);
577 (void)m_signal.fetch_add(1, std::memory_order_release);
580template <
class CompletionFunction>
581template <
class WaitFunction>
582void FlexBarrier<CompletionFunction>::AwaitArrivals(WaitFunction&& wait)
noexcept {
583 for (
auto r = m_remaining.load(std::memory_order_relaxed); ION_LIKELY((r & 0xffff'ffff) != 0);
584 r = m_remaining.load(std::memory_order_relaxed)) {
585 detail::Invoke(wait);
Token from ion::Barrier::Wait()
Definition barrier.hpp:38
Reusable thread-coordination mechanism modelled after std::barrier.
Definition barrier.hpp:90
constexpr Barrier(std::ptrdiff_t expected, CompletionFunction func=CompletionFunction()) noexcept
Construct new barrier with an expected count of expected.
Definition barrier.hpp:319
void ArriveAndWait(std::ptrdiff_t n=1) noexcept
void ArriveAndDrop() noexcept
void Wait(ArrivalToken &&token) const noexcept
static constexpr auto Max() noexcept -> std::ptrdiff_t
auto Arrive(std::ptrdiff_t n=1) noexcept -> ArrivalToken
void ArriveAndWait(InvokePolicy policy, std::uint32_t n=1u, WaitFunction &&wait=WaitFunction()) noexcept
static constexpr auto Max() noexcept -> std::uint32_t
constexpr FlexBarrier(std::uint32_t expected, CompletionFunction func=CompletionFunction()) noexcept
Construct new barrier with an expected count of expected.
Definition barrier.hpp:445
InvokePolicy
Controls barrier completion function invocation policy.
Definition barrier.hpp:198
@ Allowed
Completion function invocation is allowed.
Definition barrier.hpp:202
@ Disallowed
Completion function invocation is not allowed.
Definition barrier.hpp:207
auto Wait(SignalToken< T > &token) noexcept -> T
Wait until signal source changes and return current value.