jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
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 = default;
68
69 /**
70 * Initialize instance with given counter.
71 *
72 * Compatible with std::latch.
73 *
74 * @param count_
75 */
76 latch(const size_t count_) noexcept
77 : count(count_) {}
78
79 /**
80 * No copy constructor nor move constructor.
81 *
82 * Compatible with std::latch.
83 *
84 * @param o
85 */
86 latch(const latch& o) = delete;
87
88 /**
89 * Return the current atomic internal counter.
90 *
91 * Extension of std::latch.
92 */
93 size_t value() const noexcept { return count; }
94
95 /**
96 * Atomically decrements the internal counter by n
97 * and notifies all blocked wait() threads if zero is reached.
98 *
99 * If n is greater than the value of the internal counter, the counter is set to zero.
100 *
101 * This operation strongly happens-before all calls that are unblocked on this latch.
102 *
103 * Compatible with std::latch.
104 *
105 * @param n the value by which the internal counter is decreased, defaults to 1
106 */
107 void count_down(const size_t n = 1) noexcept {
108 bool notify;
109 {
110 std::unique_lock<std::mutex> lock(mtx_cd); // Avoid data-race on concurrent count_down() and wait*() calls
111 if( n < count ) {
112 count = count - n;
113 notify = false;
114 } else {
115 count = 0;
116 notify = true;
117 }
118 }
119 if( notify ) {
120 cv.notify_all();
121 }
122 }
123
124 /**
125 * Atomically increments the internal counter by n, i.e. adds value to count down.
126 *
127 * If internal counter + n is greater than the maximum value of the internal counter, the counter is set to its maximum.
128 *
129 * This operation strongly happens-before all calls that are unblocked on this latch.
130 *
131 * Extension of std::latch.
132 *
133 * @param n the value by which the internal counter is increased, defaults to 1
134 */
135 void count_up(const size_t n = 1) noexcept {
136 std::unique_lock<std::mutex> lock(mtx_cd); // Avoid data-race on concurrent count_down() and wait*() calls
137 if( n <= std::numeric_limits<std::size_t>::max() - count ) {
138 count = count + n;
139 } else {
140 count = std::numeric_limits<std::size_t>::max();
141 }
142 }
143
144 /**
145 * Atomically set the internal counter by n.
146 *
147 * This operation strongly happens-before all calls that are unblocked on this latch.
148 *
149 * Extension of std::latch.
150 *
151 * @param n the value to be assigned to the internal counter
152 */
153 void set(const size_t n) noexcept {
154 std::unique_lock<std::mutex> lock(mtx_cd); // Avoid data-race on concurrent count_down() and wait*() calls
155 count = n;
156 }
157
158 /**
159 * Returns true only if the internal counter has reached zero.
160 *
161 * Compatible with std::latch.
162 */
163 bool try_wait() const noexcept {
164 return 0 == count;
165 }
166
167 /**
168 * Blocks the calling thread until the internal counter reaches 0.
169 *
170 * If the internal counter is zero already, returns immediately.
171 *
172 * Compatible with std::latch.
173 */
174 void wait() const noexcept {
175 if( 0 < count ) {
176 std::unique_lock<std::mutex> lock(mtx_cd);
177 while( 0 < count ) {
178 cv.wait(lock);
179 }
180 }
181 }
182
183 /**
184 * Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until the counter reaches zero.
185 *
186 * Equivalent to `count_down(n); wait();`.
187 *
188 * Compatible with std::latch.
189 *
190 * @param n the value by which the internal counter is decreased, defaults to 1
191 */
192 void arrive_and_wait(const size_t n = 1) noexcept {
193 count_down(n);
194 wait();
195 }
196
197 /**
198 * Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expired.
199 *
200 * If the internal counter is zero already, returns immediately.
201 *
202 * Implementation uses wait_for() w/ a monotonic clock and fraction_i64.
203 *
204 * Extension of std::latch.
205 *
206 * @param timeout_duration maximum duration in fractions of seconds to wait
207 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
208 */
209 bool wait_for(const fraction_i64& timeout_duration) const noexcept {
210 if( 0 < count ) {
211 std::unique_lock<std::mutex> lock(mtx_cd);
212 const fraction_timespec timeout_time = getMonotonicTime() + timeout_duration;
213 while( 0 < count ) {
214 std::cv_status s = wait_until(cv, lock, timeout_time);
215 if( 0 == count ) {
216 return true;
217 }
218 if( std::cv_status::timeout == s ) {
219 return false;
220 }
221 }
222 }
223 return true;
224 }
225
226 /**
227 * Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until the counter reaches zero
228 * or the given timeout duration has expired.
229 *
230 * Equivalent to `count_down(n); wait(timeout_duration);`.
231 *
232 * Implementation uses `std::chrono::steady_clock::now()` and fraction_i64.
233 *
234 * Extension of std::latch.
235 *
236 * @param timeout_duration maximum duration in fractions of seconds to wait
237 * @param n the value by which the internal counter is decreased, defaults to 1
238 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
239 */
240 bool arrive_and_wait_for(const fraction_i64& timeout_duration, const size_t n = 1) noexcept {
241 count_down(n);
242 return wait_for(timeout_duration);
243 }
244
245 /**
246 * Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expired.
247 *
248 * If the internal counter is zero already, returns immediately.
249 *
250 * Implementation uses `std::chrono::steady_clock::now()`.
251 *
252 * Extension of std::latch.
253 *
254 * @tparam Rep
255 * @tparam Period
256 * @param timeout_duration maximum duration to wait
257 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
258 */
259 template<typename Rep, typename Period>
260 bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const noexcept {
261 if( 0 < count ) {
262 std::unique_lock<std::mutex> lock(mtx_cd);
263 std::chrono::steady_clock::time_point timeout_time = std::chrono::steady_clock::now() + timeout_duration;
264 while( 0 < count ) {
265 std::cv_status s = cv.wait_until(lock, timeout_time );
266 if( 0 == count ) {
267 return true;
268 }
269 if( std::cv_status::timeout == s ) {
270 return false;
271 }
272 }
273 }
274 return true;
275 }
276
277 /**
278 * Atomically decrements the internal counter by n and (if necessary) blocks the calling thread until the counter reaches zero
279 * or the given timeout duration has expired.
280 *
281 * Equivalent to `count_down(n); wait(timeout_duration);`.
282 *
283 * Implementation uses `std::chrono::steady_clock::now()`.
284 *
285 * Extension of std::latch.
286 *
287 * @tparam Rep
288 * @tparam Period
289 * @param timeout_duration maximum duration to wait
290 * @param n the value by which the internal counter is decreased, defaults to 1
291 * @return true if internal counter has reached zero, otherwise a timeout has occurred.
292 */
293 template<typename Rep, typename Period>
294 bool arrive_and_wait_for(const std::chrono::duration<Rep, Period>& timeout_duration, const size_t n = 1) noexcept {
295 count_down(n);
296 return wait_for(timeout_duration);
297 }
298 };
299
300 /**@}*/
301
302} /* namespace jau */
303
304#endif /* JAU_LATCH_HPP_ */
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:192
latch(const latch &o)=delete
No copy constructor nor move constructor.
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:93
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:294
bool try_wait() const noexcept
Returns true only if the internal counter has reached zero.
Definition latch.hpp:163
void set(const size_t n) noexcept
Atomically set the internal counter by n.
Definition latch.hpp:153
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:209
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:107
latch() noexcept=default
Initialize instance with counter zero.
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:240
void count_up(const size_t n=1) noexcept
Atomically increments the internal counter by n, i.e.
Definition latch.hpp:135
void wait() const noexcept
Blocks the calling thread until the internal counter reaches 0.
Definition latch.hpp:174
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:260
ordered_atomic< std::size_t, std::memory_order_seq_cst > sc_atomic_size_t
SC atomic integral scalar size_t.
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
fraction< int64_t > fraction_i64
fraction using int64_t as integral type
__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...