jaulib v1.3.0
Jau Support Library (C++, Java, ..)
latch.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef JAU_LATCH_HPP_
26#define JAU_LATCH_HPP_
27
28#include <cstdint>
29#include <mutex>
30#include <condition_variable>
31
33#include <jau/fraction_type.hpp>
34#include <jau/basic_types.hpp>
35
36namespace jau {
37
38 /** \addtogroup Concurrency
39 *
40 * @{
41 */
42
43 /**
44 * Inspired by std::latch of C++20
45 *
46 * Adds count_up() as an extension, allowing to dynamically add *events* to required to complete.
47 *
48 * @see https://en.cppreference.com/w/cpp/thread/latch
49 */
50 class latch {
51 private:
52 mutable std::mutex mtx_cd;
53 mutable std::condition_variable cv;
55
56 public:
57 /** Returns the maximum value of the internal counter supported by the implementation. */
58 static constexpr size_t max() noexcept { return std::numeric_limits<size_t>::max(); }
59
60 /**
61 * Initialize instance with counter zero.
62 *
63 * Useful for member instances in combination with count_up() or with set() before count_down().
64 *
65 * Extension of std::latch.
66 */
67 latch() noexcept
68 : count(0) {}
69
70 /**
71 * Initialize instance with given counter.
72 *
73 * Compatible with std::latch.
74 *
75 * @param count_
76 */
77 latch(const size_t count_) noexcept
78 : count(count_) {}
79
80 /**
81 * No copy constructor nor move constructor.
82 *
83 * Compatible with std::latch.
84 *
85 * @param o
86 */
87 latch(const latch& o) = delete;
88
89 /**
90 * Return the current atomic internal counter.
91 *
92 * Extension of std::latch.
93 */
94 size_t value() const noexcept { return count; }
95
96 /**
97 * Atomically decrements the internal counter by n
98 * and notifies all blocked wait() threads if zero is reached.
99 *
100 * If n is greater than the value of the internal counter, the counter is set to zero.
101 *
102 * This operation strongly happens-before all calls that are unblocked on this latch.
103 *
104 * Compatible with std::latch.
105 *
106 * @param n the value by which the internal counter is decreased, defaults to 1
107 */
108 void count_down(const size_t n = 1) noexcept {
109 bool notify;
110 {
111 std::unique_lock<std::mutex> lock(mtx_cd); // Avoid data-race on concurrent count_down() and wait*() calls
112 if( n < count ) {
113 count = count - n;
114 notify = false;
115 } else {
116 count = 0;
117 notify = true;
118 }
119 }
120 if( notify ) {
121 cv.notify_all();
122 }
123 }
124
125 /**
126 * Atomically increments the internal counter by n, i.e. adds value to count down.
127 *
128 * If internal counter + n is greater than the maximum value of the internal counter, the counter is set to its maximum.
129 *
130 * This operation strongly happens-before all calls that are unblocked on this latch.
131 *
132 * Extension of std::latch.
133 *
134 * @param n the value by which the internal counter is increased, defaults to 1
135 */
136 void count_up(const size_t n = 1) noexcept {
137 std::unique_lock<std::mutex> lock(mtx_cd); // Avoid data-race on concurrent count_down() and wait*() calls
138 if( n <= std::numeric_limits<std::size_t>::max() - count ) {
139 count = count + n;
140 } else {
142 }
143 }
144
145 /**
146 * Atomically set the internal counter by n.
147 *
148 * This operation strongly happens-before all calls that are unblocked on this latch.
149 *
150 * Extension of std::latch.
151 *
152 * @param n the value to be assigned to the internal counter
153 */
154 void set(const size_t n) noexcept {
155 std::unique_lock<std::mutex> lock(mtx_cd); // Avoid data-race on concurrent count_down() and wait*() calls
156 count = n;
157 }
158
159 /**
160 * Returns true only if the internal counter has reached zero.
161 *
162 * Compatible with std::latch.
163 */
164 bool try_wait() const noexcept {
165 return 0 == count;
166 }
167
168 /**
169 * Blocks the calling thread until the internal counter reaches 0.
170 *
171 * If the internal counter is zero already, returns immediately.
172 *
173 * Compatible with std::latch.
174 */
175 void wait() const noexcept {
176 if( 0 < count ) {
177 std::unique_lock<std::mutex> lock(mtx_cd);
178 while( 0 < count ) {
179 cv.wait(lock);
180 }
181 }
182 }
183
184 /**
185 * Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until the counter reaches zero.
186 *
187 * Equivalent to `count_down(n); wait();`.
188 *
189 * Compatible with std::latch.
190 *
191 * @param n the value by which the internal counter is decreased, defaults to 1
192 */
193 void arrive_and_wait(const size_t n = 1) noexcept {
194 count_down(n);
195 wait();
196 }
197
198 /**
199 * Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expired.
200 *
201 * If the internal counter is zero already, returns immediately.
202 *
203 * Implementation uses wait_for() w/ a monotonic clock and fraction_i64.
204 *
205 * Extension of std::latch.
206 *
207 * @param timeout_duration maximum duration in fractions of seconds to wait
208 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
209 */
210 bool wait_for(const fraction_i64& timeout_duration) const noexcept {
211 if( 0 < count ) {
212 std::unique_lock<std::mutex> lock(mtx_cd);
213 const fraction_timespec timeout_time = getMonotonicTime() + timeout_duration;
214 while( 0 < count ) {
215 std::cv_status s = wait_until(cv, lock, timeout_time);
216 if( 0 == count ) {
217 return true;
218 }
219 if( std::cv_status::timeout == s ) {
220 return false;
221 }
222 }
223 }
224 return true;
225 }
226
227 /**
228 * Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until the counter reaches zero
229 * or the given timeout duration has expired.
230 *
231 * Equivalent to `count_down(n); wait(timeout_duration);`.
232 *
233 * Implementation uses `std::chrono::steady_clock::now()` and fraction_i64.
234 *
235 * Extension of std::latch.
236 *
237 * @param timeout_duration maximum duration in fractions of seconds to wait
238 * @param n the value by which the internal counter is decreased, defaults to 1
239 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
240 */
241 bool arrive_and_wait_for(const fraction_i64& timeout_duration, const size_t n = 1) noexcept {
242 count_down(n);
243 return wait_for(timeout_duration);
244 }
245
246 /**
247 * Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expired.
248 *
249 * If the internal counter is zero already, returns immediately.
250 *
251 * Implementation uses `std::chrono::steady_clock::now()`.
252 *
253 * Extension of std::latch.
254 *
255 * @tparam Rep
256 * @tparam Period
257 * @param timeout_duration maximum duration to wait
258 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
259 */
260 template<typename Rep, typename Period>
261 bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const noexcept {
262 if( 0 < count ) {
263 std::unique_lock<std::mutex> lock(mtx_cd);
264 std::chrono::steady_clock::time_point timeout_time = std::chrono::steady_clock::now() + timeout_duration;
265 while( 0 < count ) {
266 std::cv_status s = cv.wait_until(lock, timeout_time );
267 if( 0 == count ) {
268 return true;
269 }
270 if( std::cv_status::timeout == s ) {
271 return false;
272 }
273 }
274 }
275 return true;
276 }
277
278 /**
279 * Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until the counter reaches zero
280 * or the given timeout duration has expired.
281 *
282 * Equivalent to `count_down(n); wait(timeout_duration);`.
283 *
284 * Implementation uses `std::chrono::steady_clock::now()`.
285 *
286 * Extension of std::latch.
287 *
288 * @tparam Rep
289 * @tparam Period
290 * @param timeout_duration maximum duration to wait
291 * @param n the value by which the internal counter is decreased, defaults to 1
292 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
293 */
294 template<typename Rep, typename Period>
295 bool arrive_and_wait_for(const std::chrono::duration<Rep, Period>& timeout_duration, const size_t n = 1) noexcept {
296 count_down(n);
297 return wait_for(timeout_duration);
298 }
299 };
300
301 /**@}*/
302
303} /* namespace jau */
304
305#endif /* JAU_LATCH_HPP_ */
Inspired by std::latch of C++20.
Definition: latch.hpp:50
void arrive_and_wait(const size_t n=1) noexcept
Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until th...
Definition: latch.hpp:193
latch(const latch &o)=delete
No copy constructor nor move constructor.
latch(const size_t count_) noexcept
Initialize instance with given counter.
Definition: latch.hpp:77
static constexpr size_t max() noexcept
Returns the maximum value of the internal counter supported by the implementation.
Definition: latch.hpp:58
size_t value() const noexcept
Return the current atomic internal counter.
Definition: latch.hpp:94
bool arrive_and_wait_for(const std::chrono::duration< Rep, Period > &timeout_duration, const size_t n=1) noexcept
Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until th...
Definition: latch.hpp:295
bool try_wait() const noexcept
Returns true only if the internal counter has reached zero.
Definition: latch.hpp:164
void set(const size_t n) noexcept
Atomically set the internal counter by n.
Definition: latch.hpp:154
bool wait_for(const fraction_i64 &timeout_duration) const noexcept
Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expi...
Definition: latch.hpp:210
void count_down(const size_t n=1) noexcept
Atomically decrements the internal counter by n and notifies all blocked wait() threads if zero is re...
Definition: latch.hpp:108
bool arrive_and_wait_for(const fraction_i64 &timeout_duration, const size_t n=1) noexcept
Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until th...
Definition: latch.hpp:241
latch() noexcept
Initialize instance with counter zero.
Definition: latch.hpp:67
void count_up(const size_t n=1) noexcept
Atomically increments the internal counter by n, i.e.
Definition: latch.hpp:136
void wait() const noexcept
Blocks the calling thread until the internal counter reaches 0.
Definition: latch.hpp:175
bool wait_for(const std::chrono::duration< Rep, Period > &timeout_duration) const noexcept
Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expi...
Definition: latch.hpp:261
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
Definition: basic_types.cpp:52
constexpr T max(const T x, const T y) noexcept
Returns the maximum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:191
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
std::cv_status wait_until(std::condition_variable &cv, std::unique_lock< std::mutex > &lock, const fraction_timespec &absolute_time, const bool monotonic=true) noexcept
wait_until causes the current thread to block until the condition variable is notified,...
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...