49#include <ion/detail/macros.hpp>
88 [[nodiscard]]
auto Load() const noexcept -> ValueType;
96 void Store(ValueType value) noexcept;
131 using SignalType = std::atomic<ValueType>;
132 static_assert(SignalType::is_always_lock_free);
171template <class T, class UnaryOperation>
210 std::optional<ValueType> last = std::nullopt) noexcept;
239 [[nodiscard]] constexpr auto
IsValid() const noexcept ->
bool;
249 [[nodiscard]] auto
Load() const noexcept -> ValueType;
259 [[nodiscard]] constexpr auto
GetLast() const noexcept -> ValueType;
270 constexpr
void SetLast(ValueType last) noexcept;
287 ValueType m_last = ValueType();
304[[nodiscard]] constexpr auto
321[[nodiscard]] constexpr auto
342template <
class T1,
class T2>
363 explicit StatusTag() =
default;
376struct DifferenceTag {
377 explicit DifferenceTag() =
default;
438template <
class T,
class Predicate>
486template <
class T,
class Predicate>
544template <
class T,
class Predicate>
571template <
class T1,
class T2>
588template <
class T1,
class T2>
606template <
class T1,
class T2>
618 return m_signal.load(std::memory_order_relaxed);
623 m_signal.store(value, std::memory_order_relaxed);
628 return m_signal.compare_exchange_weak(expected, desired, std::memory_order_relaxed);
634 return m_signal.compare_exchange_strong(expected, desired, std::memory_order_relaxed);
637template <
class T,
class UnaryOperation>
641 std::is_nothrow_invocable_r<T, UnaryOperation, T>::value,
642 "UnaryOperation must be noexcept invocable with signature `T op(T const&) noexcept`");
643 auto expected = signal.Load();
644 T desired = expected;
645 while (!signal.CompareExchangeWeak(expected, (desired = op(expected)))) {
653 : m_signal(other.m_signal), m_last(other.m_last) {
654 other.m_signal =
nullptr;
658 : m_signal(signal), m_last(last.value_or(ValueType{})) {
659 if (!last.has_value() && m_signal) {
660 m_last = m_signal->Load();
666 return m_signal !=
nullptr;
672 return m_signal->Load();
697auto SignalToken<T>::operator=(SignalToken&& other)
noexcept -> SignalToken& {
698 m_signal = other.m_signal;
699 m_last = other.m_last;
700 other.m_signal =
nullptr;
705constexpr bool operator==(SignalToken<T>
const& lhs, SignalToken<T>
const& rhs)
noexcept {
706 return lhs.GetSource() == rhs.GetSource() && lhs.GetLast() == rhs.GetLast();
710constexpr bool operator!=(SignalToken<T>
const& lhs, SignalToken<T>
const& rhs)
noexcept {
711 return !(lhs == rhs);
715auto Wait(SignalToken<T>& token)
noexcept -> T {
716 return Wait(StatusTag{}, token).current;
719template <
class T,
class Predicate>
720auto Wait(SignalToken<T>& token, Predicate&& stop_waiting)
noexcept -> T {
721 return Wait(StatusTag{}, token, std::forward<Predicate>(stop_waiting)).current;
725auto Wait(StatusTag, SignalToken<T>& token)
noexcept -> Status<T> {
726 auto previous = token.GetLast();
727 auto current = token.Load();
728 for (; current == previous; current = token.Load()) {
732 token.SetLast(current);
733 return {previous, current};
736template <
class T,
class Predicate>
737auto Wait(StatusTag, SignalToken<T>& token, Predicate&& stop_waiting)
noexcept -> Status<T> {
738 static_assert(std::is_invocable_r<bool, Predicate, T>::value,
739 "Predicate must be invocable with signature `bool(T current_value)`");
740 auto previous = token.GetLast();
741 auto current = token.Load();
742 while (!stop_waiting(current)) {
743 current =
Wait(token);
747 token.SetLast(current);
748 return {previous, current};
752auto Wait(DifferenceTag, SignalToken<T>& token)
noexcept -> T {
753 static_assert(std::is_same_v<T,
decltype(T() - T())>);
754 auto [previous, current] =
Wait(StatusTag{}, token);
755 return current - previous;
758template <
class T,
class Predicate>
759auto Wait(DifferenceTag, SignalToken<T>& token, Predicate&& stop_waiting)
noexcept -> T {
760 static_assert(std::is_same_v<T,
decltype(T() - T())>);
761 auto [previous, current] =
Wait(StatusTag{}, token, std::forward<Predicate>(stop_waiting));
762 return current - previous;
765template <
class T1,
class T2>
766auto WaitAny(StatusTag, SignalToken<T1>& token1, SignalToken<T2>& token2)
noexcept
767 -> WaitAnyResult<Status<T1>, Status<T2>> {
770 Status<T1> first = {token1.GetLast(), token1.Load()};
771 Status<T2> second = {token2.GetLast(), token2.Load()};
774 if (first.current != first.previous) {
778 if (second.current != second.previous) {
784 first.current = token1.Load();
785 second.current = token2.Load();
788 token1.SetLast(first.current);
789 token2.SetLast(second.current);
790 return {index, {first, second}};
793template <
class T1,
class T2>
794auto WaitAny(DifferenceTag, SignalToken<T1>& token1, SignalToken<T2>& token2)
noexcept
795 -> WaitAnyResult<T1, T2> {
796 auto r =
WaitAny(StatusTag{}, token1, token2);
798 {std::get<0>(r.result).current - std::get<0>(r.result).previous,
799 std::get<1>(r.result).current - std::get<1>(r.result).previous}};
802template <
class T1,
class T2>
803auto WaitAny(SignalToken<T1>& token1, SignalToken<T2>& token2)
noexcept -> WaitAnyResult<T1, T2> {
804 auto r =
WaitAny(StatusTag{}, token1, token2);
806 return {r.index, {std::get<0>(r.result).current, std::get<1>(r.result).current}};
Atomic signal source.
Definition signal.hpp:66
auto Load() const noexcept -> ValueType
Load signal value.
SignalSource(SignalSource const &)=delete
SignalSource requires stable address so copy and move operations are disabled.
SignalSource(ValueType initial=ValueType{}) noexcept
Construct signal source.
void Store(ValueType value) noexcept
Store signal value.
auto CompareExchangeWeak(ValueType &expected, ValueType desired) noexcept -> bool
Set signal value using read-modify-write semantics.
auto CompareExchangeStrong(ValueType &expected, ValueType desired) noexcept -> bool
Set signal value using read-modify-write semantics.
auto Load() const noexcept -> ValueType
auto Update() noexcept -> ValueType
constexpr void SetLast(ValueType last) noexcept
Set last observed value.
SignalToken() noexcept=default
Default-initialized token.
constexpr auto IsValid() const noexcept -> bool
constexpr auto GetLast() const noexcept -> ValueType
Get last observed value.
constexpr auto GetSource() const -> SignalSource< ValueType > const *
auto Wait(DifferenceTag, SignalToken< T > &token) noexcept -> T
Wait until signal source changes and return difference (current - previous) value.
constexpr auto operator!=(SignalToken< T > const &lhs, SignalToken< T > const &rhs) noexcept -> bool
Inequality comparison.
auto WaitAny(DifferenceTag, SignalToken< T1 > &token1, SignalToken< T2 > &token2) noexcept -> WaitAnyResult< T1, T2 >
Wait for either of two signals to change.
auto Wait(DifferenceTag, SignalToken< T > &token, Predicate &&stop_waiting) noexcept -> T
Wait until signal source changes and return difference (current - previous) value.
auto WaitAny(SignalToken< T1 > &token1, SignalToken< T2 > &token2) noexcept -> WaitAnyResult< T1, T2 >
Wait for either of two signals to change.
auto CompareExchangeTransform(SignalSource< ValueType > &signal, UnaryOperation op) noexcept -> ValueType
auto Wait(StatusTag, SignalToken< T > &token) noexcept -> Status< T >
Wait until signal source changes and return current and previous values.
constexpr auto operator==(SignalToken< T > const &lhs, SignalToken< T > const &rhs) noexcept -> bool
Equality comparison.
auto Wait(StatusTag, SignalToken< T > &token, Predicate &&stop_waiting) noexcept -> Status< T >
Wait with predicate.
auto WaitAny(StatusTag, SignalToken< T1 > &token1, SignalToken< T2 > &token2) noexcept -> WaitAnyResult< Status< T1 >, Status< T2 > >
Wait for either of two signals to change.
auto Wait(SignalToken< T > &token) noexcept -> T
Wait until signal source changes and return current value.
constexpr DifferenceTag difference_tag
Constant instance of DifferenceTag.
Definition signal.hpp:384
constexpr StatusTag status_tag
Constant instance of StatusTag.
Definition signal.hpp:370
auto Wait(SignalToken< T > &token, Predicate &&stop_waiting) noexcept -> T
Wait with predicate.
Select algorithm overload that returns signal difference.
Definition signal.hpp:376
Select algorithm overload that returns signal status.
Definition signal.hpp:362
Represents signal source previous (old) and current (new) value.
Definition signal.hpp:329
Represents wait result from one of multiple possible signals.
Definition signal.hpp:343
std::tuple< T1, T2 > result
Result of wait operation.
Definition signal.hpp:351
std::size_t index
Index in result that unblocked WaitAny()
Definition signal.hpp:347