YACLib
C++ library for concurrent tasks execution
Loading...
Searching...
No Matches
wait_group.hpp
Go to the documentation of this file.
1#pragma once
2
6#include <yaclib/config.hpp>
9
10#include <cstddef>
11
12namespace yaclib {
13
14/**
15 * An object that allows you to Add some amount of async operations and then Wait for it to be Done
16 */
17template <typename Event = OneShotEvent>
19 public:
20 explicit WaitGroup(std::size_t count = 0) noexcept : _event{count} {
21 }
22
23 /**
24 * Add some amount of async operations
25 *
26 * Can be called parallel with Add, Done,
27 * and with Wait, but only if you call it when some Add not Done yet
28 *
29 * \param count of async operations
30 */
31 YACLIB_INLINE void Add(std::size_t count = 1) noexcept {
32 _event.Add(count);
33 }
34
35 /**
36 * Done some Add-ed async operations
37 *
38 * \param count of async operations
39 */
40 YACLIB_INLINE void Done(std::size_t count = 1) noexcept {
41 _event.Sub(count);
42 }
43
44 YACLIB_INLINE std::size_t Count(std::memory_order order = std::memory_order_relaxed) const noexcept {
45 return _event.Get(order);
46 }
47
48 /**
49 * Consume \ref Future by WaitGroup with auto Done
50 *
51 * Also \see Add
52 *
53 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Consume
54 * \param futures to wait
55 */
56 template <bool NeedAdd = true, typename... V, typename... T>
60
61 /**
62 * Consume \ref Future by WaitGroup with auto Done
63 *
64 * Also \see Add
65 *
66 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Consume
67 * \param begin iterator to futures to Add
68 * \param end iterator to futures to Add
69 */
70 template <bool NeedAdd = true, typename It, typename Sentinel,
71 typename = std::enable_if_t<is_input_range_pair_v<It, Sentinel>>>
72 YACLIB_INLINE void Consume(It begin, Sentinel end) noexcept {
73 static_assert(
75 "Use Consume(begin, std::distance(begin, end)) instead"); // We don't use std::distance because we want to alert
76 // the user to the fact that it can be expensive.
77
78 Consume<NeedAdd>(begin, static_cast<std::size_t>(end - begin));
79 }
80
81 /**
82 * Consume \ref Future by WaitGroup with auto Done
83 *
84 * Also \see Add
85 *
86 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Consume
87 * \param range of futures to Add
88 */
89 template <bool NeedAdd = true, typename Range, typename = std::enable_if_t<is_input_range_v<Range>>>
90 YACLIB_INLINE void Consume(Range&& range) noexcept {
91 Consume<NeedAdd>(std::begin(range), std::end(range));
92 }
93
94 /**
95 * Consume \ref Future by WaitGroup with auto Done
96 *
97 * Also \see Add
98 *
99 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Consume
100 * \param begin iterator to futures to Add
101 * \param count of futures to Add
102 */
103 template <bool NeedAdd = true, typename It, typename = std::enable_if_t<is_input_iterator_v<It>>>
104 YACLIB_INLINE void Consume(It begin, std::size_t count) noexcept {
106 }
107
108 /**
109 * Attach \ref Future to WaitGroup with auto Done
110 *
111 * Also \see Add
112 *
113 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
114 * \param futures to wait
115 */
116 template <bool NeedAdd = true, typename... V, typename... T>
120
121 /**
122 * Attach \ref Future to WaitGroup with auto Done
123 *
124 * Also \see Add
125 *
126 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
127 * \param begin iterator to futures to Add
128 * \param end iterator to futures to Add
129 */
130 template <bool NeedAdd = true, typename It, typename Sentinel,
131 typename = std::enable_if_t<is_input_range_pair_v<It, Sentinel>>>
132 YACLIB_INLINE void Attach(It begin, Sentinel end) noexcept {
133 static_assert(
135 "Use Attach(begin, std::distance(begin, end)) instead"); // We don't use std::distance because we want to alert
136 // the user to the fact that it can be expensive.
137
138 Attach<NeedAdd>(begin, static_cast<std::size_t>(end - begin));
139 }
140
141 /**
142 * Attach \ref Future to WaitGroup with auto Done
143 *
144 * Also \see Add
145 *
146 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
147 * \param range of futures to Add
148 */
149 template <bool NeedAdd = true, typename Range, typename = std::enable_if_t<is_input_range_v<Range>>>
150 YACLIB_INLINE void Attach(Range&& range) noexcept {
151 Attach<NeedAdd>(std::begin(range), std::end(range));
152 }
153
154 /**
155 * Attach \ref Future to WaitGroup with auto Done
156 *
157 * Also \see Add
158 *
159 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
160 * \param begin iterator to futures to Add
161 * \param count of futures to Add
162 */
163 template <bool NeedAdd = true, typename It, typename = std::enable_if_t<is_input_iterator_v<It>>>
164 YACLIB_INLINE void Attach(It begin, std::size_t count) noexcept {
166 }
167
168 /**
169 * TODO
170 */
172 _event.Wait();
173 }
174 /**
175 * TODO
176 */
177 template <typename Rep, typename Period>
178 YACLIB_INLINE bool WaitFor(const std::chrono::duration<Rep, Period>& timeout_duration) {
179 return _event.WaitFor(timeout_duration);
180 }
181
182 /**
183 * TODO
184 */
185 template <typename Clock, typename Duration>
186 YACLIB_INLINE bool WaitUntil(const std::chrono::time_point<Clock, Duration>& timeout_time) {
187 return _event.WaitUntil(timeout_time);
188 }
189
190#if YACLIB_CORO != 0
191 /**
192 * See OneShotEvent::Await
193 */
195 return _event.AwaitInline();
196 }
197
198 /**
199 * See OneShotEvent::Await
200 */
202 return _event.AwaitSticky();
203 }
204
205 /**
206 * See OneShotEvent::AwaitOn
207 */
208 YACLIB_INLINE auto AwaitOn(IExecutor& e) noexcept {
209 return _event.AwaitOn(e);
210 }
211
212 /**
213 * just shortcut for co_await wait_group.AwaitInline();
214 *
215 * TODO(MBkkt) move all shortcut to AwaitSticky
216 */
217 YACLIB_INLINE auto operator co_await() noexcept {
218 return AwaitInline();
219 }
220#endif
221
222 /**
223 * Reinitializes WaitGroup, semantically the same as `*this = {};`
224 *
225 * If you don't explicitly call this method,
226 * then after the first one, Wait will always return immediately.
227 *
228 * \note Not thread-safe
229 */
230 YACLIB_INLINE void Reset(std::size_t count = 0) noexcept {
231 _event.Reset();
232 _event.count.store(count, std::memory_order_relaxed);
233 }
234
235 private:
236 template <bool NeedMove, bool NeedAdd, typename... Cores>
237 YACLIB_INLINE void InsertCore(Cores&... cores) noexcept {
238 static_assert(sizeof...(cores) >= 1, "Number of futures must be at least one");
239 static_assert((... && std::is_same_v<detail::BaseCore, Cores>),
240 "Futures must be Future in WaitGroup::Consume/Attach function");
241 auto range = [&](auto&& func) noexcept {
242 return (... + static_cast<std::size_t>(func(cores)));
243 };
245 }
246
247 template <bool NeedMove, bool NeedAdd, typename It>
248 YACLIB_INLINE void InsertIt(It it, std::size_t count) noexcept {
250 "WaitGroup::Consume/Attach function Iterator must be point to some Future");
251 if (count == 0) {
252 return;
253 }
254 auto range = [&](auto&& func) noexcept {
255 std::size_t wait_count = 0;
256 for (std::size_t i = 0; i != count; ++i) {
257 if constexpr (NeedMove) {
258 wait_count += static_cast<std::size_t>(func(*it->GetCore().Release()));
259 } else {
260 wait_count += static_cast<std::size_t>(func(*it->GetCore()));
261 }
262 ++it;
263 }
264 return wait_count;
265 };
267 }
268
269 template <bool NeedMove, bool NeedAdd, typename Range>
270 void InsertRange(const Range& range, std::size_t count) noexcept {
271 if constexpr (NeedAdd) {
272 Add(count);
273 }
274 const auto wait_count = range([&](detail::BaseCore& core) noexcept {
275 detail::UniqueHandle handle{core};
276 if constexpr (NeedMove) {
277 if (handle.SetCallback(_event.GetDrop())) {
278 return true;
279 }
280 core.DecRef();
281 return false;
282 } else {
283 return handle.SetCallback(_event.GetCall());
284 }
285 });
286 if (count != wait_count) { // TODO(MBkkt) is it necessary?
287 Done(count - wait_count);
288 }
289 }
290 detail::MultiEvent<Event, detail::AtomicCounter, detail::CallCallback, detail::DropCallback> _event;
291};
292
293extern template class WaitGroup<OneShotEvent>;
294
295} // namespace yaclib
Provides a mechanism to access the result of async operations.
Definition future.hpp:21
An object that allows you to Add some amount of async operations and then Wait for it to be Done.
YACLIB_INLINE void Reset(std::size_t count=0) noexcept
Reinitializes WaitGroup, semantically the same as *this = {};
YACLIB_INLINE void Wait() noexcept
TODO.
YACLIB_INLINE void Done(std::size_t count=1) noexcept
Done some Add-ed async operations.
YACLIB_INLINE void Add(std::size_t count=1) noexcept
Add some amount of async operations.
YACLIB_INLINE void Attach(It begin, Sentinel end) noexcept
Attach Future to WaitGroup with auto Done.
YACLIB_INLINE void Consume(It begin, std::size_t count) noexcept
Consume Future by WaitGroup with auto Done.
YACLIB_INLINE void Consume(FutureBase< V, T > &&... futures) noexcept
Consume Future by WaitGroup with auto Done.
YACLIB_INLINE std::size_t Count(std::memory_order order=std::memory_order_relaxed) const noexcept
YACLIB_INLINE bool WaitFor(const std::chrono::duration< Rep, Period > &timeout_duration)
TODO.
YACLIB_INLINE void Attach(Range &&range) noexcept
Attach Future to WaitGroup with auto Done.
WaitGroup(std::size_t count=0) noexcept
YACLIB_INLINE bool WaitUntil(const std::chrono::time_point< Clock, Duration > &timeout_time)
TODO.
YACLIB_INLINE void Consume(It begin, Sentinel end) noexcept
Consume Future by WaitGroup with auto Done.
YACLIB_INLINE void Attach(FutureBase< V, T > &... futures) noexcept
Attach Future to WaitGroup with auto Done.
YACLIB_INLINE void Attach(It begin, std::size_t count) noexcept
Attach Future to WaitGroup with auto Done.
YACLIB_INLINE void Consume(Range &&range) noexcept
Consume Future by WaitGroup with auto Done.
Contract< V, T > MakeContract()
Creates related future and promise.
Definition contract.hpp:25
YACLIB_INLINE auto AwaitInline(Waited &waited) noexcept
YACLIB_INLINE auto AwaitOn(IExecutor &e, Waited &waited) noexcept
Definition await_on.hpp:11
YACLIB_INLINE auto AwaitSticky(Waited &waited) noexcept