ion 0.12.0
Atomic concurrency support library
Loading...
Searching...
No Matches
latch.hpp
Go to the documentation of this file.
1/**
2 * @file
3 * @ingroup ion_barriers
4 *
5 * @brief ion::Latch
6 *
7 * @copyright
8 * SPDX-FileCopyrightText: 2022-2024 European Southern Observatory (ESO)
9
10 * SPDX-License-Identifier: LGPL-3.0-only
11 */
12#ifndef ION_LATCH_HPP
13#define ION_LATCH_HPP
14#include <atomic>
15#include <cassert>
16#include <cstddef>
17#include <limits>
18
19#include <ion/detail/macros.hpp>
20
21namespace ion {
22
23/**
24 * Single use thread-coordination mechanism modelled after `std::latch`.
25 *
26 * Modelled after C++20 std::latch but does not perform system calls or yields and as such is more
27 * suitable in a real-time environment. Notable differences:
28 * - Methods are noexcept
29 *
30 * Latch has two states:
31 * 1. *arrival*, when threads fetch and decrement the expected count
32 * 2. *departure*, when all threads have arrived and will depart.
33 *
34 * After *departure* the only allowed operation is to delete the latch.
35 *
36 * @memory_order{
37 * @synchronizes_with{All calls to `ArriveAndWait()` or `CountDown()`, any calls to
38 * `ArriveAndWait()` or `Wait()`.
39 * }
40 * }
41 * @thread_safe
42 * @ingroup ion_barriers
43 * @headerfile <> <ion/latch.hpp>
44 */
45class Latch {
46public:
47 /**
48 * Construct latch with expected number of arrivals.
49 *
50 * @param expected Expected number of arrivals.
51 */
52 inline constexpr explicit Latch(std::ptrdiff_t expected) noexcept;
53
54 /**
55 * @name Latch requires stable address and is neither copyable or movable.
56 */
57 /// @{
58 Latch(Latch const&) = delete;
59 Latch& operator=(Latch const&) = delete;
60 /// @}
61
62 /**
63 * Decrements expected count by @a n without waiting.
64 *
65 * @param n the value by which the internal counter is decreased.
66 */
67 inline void CountDown(std::ptrdiff_t n = 1) noexcept;
68
69 /**
70 * Equivalent to Wait().
71 *
72 * @note Method is only provided for *conceptual* compatibility of `std::latch`.
73 *
74 * @return true
75 */
76 inline bool TryWait() const noexcept;
77
78 /**
79 * Block until expected count reaches zero.
80 */
81 inline void Wait() const noexcept;
82
83 /**
84 * Decrements expected count by @a n and waits.
85 * @pre @a n must be > 0 and <= the expected count.
86 * @param n the value by which the internal counter is decreased.
87 */
88 inline void ArriveAndWait(std::ptrdiff_t n = 1) noexcept;
89
90 /**
91 * @return Maximum possible value for expected count.
92 */
93 inline static constexpr std::ptrdiff_t Max() noexcept;
94
95private:
96 // @returns true iff this call triggered the synchronization condition.
97 ION_FORCE_INLINE inline bool Arrive(std::ptrdiff_t n) noexcept;
98
99 /**
100 * Remaining number of arrivals in current iteration.
101 */
102 std::atomic<std::ptrdiff_t> m_remaining;
103};
104
105constexpr Latch::Latch(std::ptrdiff_t expected) noexcept : m_remaining(expected) {
106 assert(expected >= 0 && expected <= Max());
107}
108
109bool Latch::Arrive(std::ptrdiff_t n) noexcept {
110 // note: Memory order release to *synchronize with* memory order acquire in Wait()
111 return m_remaining.fetch_sub(n, std::memory_order_release) == n;
112}
113
114void Latch::CountDown(std::ptrdiff_t n) noexcept {
115 (void)Arrive(n);
116}
117
118void Latch::Wait() const noexcept {
119 while (true) {
120 auto remaining = m_remaining.load(std::memory_order_relaxed);
121 if (remaining == 0) {
122 // synchronization condition readed -> synchronize with other calls to Wait()
123 std::atomic_thread_fence(std::memory_order_acquire);
124 return;
125 }
126 ION_PAUSE();
127 }
128}
129
130bool Latch::TryWait() const noexcept {
131 Wait();
132 return true;
133}
134
135constexpr std::ptrdiff_t Latch::Max() noexcept {
136 return std::numeric_limits<std::ptrdiff_t>::max();
137}
138
139void Latch::ArriveAndWait(std::ptrdiff_t n) noexcept {
140 if (Arrive(n)) {
141 // *this* thread triggered synchronization condition ->
142 // *Synchronize with* all other Wait()
143 std::atomic_thread_fence(std::memory_order_acquire);
144 return;
145 }
146 Wait();
147}
148
149} // namespace ion
150#endif // ION_LATCH_HPP
void CountDown(std::ptrdiff_t n=1) noexcept
Decrements expected count by n without waiting.
Definition latch.hpp:114
static constexpr std::ptrdiff_t Max() noexcept
Definition latch.hpp:135
bool TryWait() const noexcept
Equivalent to Wait().
Definition latch.hpp:130
void ArriveAndWait(std::ptrdiff_t n=1) noexcept
Decrements expected count by n and waits.
Definition latch.hpp:139
constexpr Latch(std::ptrdiff_t expected) noexcept
Construct latch with expected number of arrivals.
Definition latch.hpp:105
void Wait() const noexcept
Block until expected count reaches zero.
Definition latch.hpp:118
auto Wait(SignalToken< T > &token) noexcept -> T
Wait until signal source changes and return current value.