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... E>
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>
71 YACLIB_INLINE std::enable_if_t<!is_future_base_v<It>, void> Consume(It begin, It end) noexcept {
72 InsertIt<true, NeedAdd>(begin, static_cast<std::size_t>(end - begin));
73 }
74
75 /**
76 * Consume \ref Future by WaitGroup with auto Done
77 *
78 * Also \see Add
79 *
80 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Consume
81 * \param begin iterator to futures to Add
82 * \param count count of futures to Add
83 */
84 template <bool NeedAdd = true, typename It>
85 YACLIB_INLINE void Consume(It begin, std::size_t count) noexcept {
87 }
88
89 /**
90 * Attach \ref Future to WaitGroup with auto Done
91 *
92 * Also \see Add
93 *
94 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
95 * \param futures to wait
96 */
97 template <bool NeedAdd = true, typename... V, typename... E>
101
102 /**
103 * Attach \ref Future to WaitGroup with auto Done
104 *
105 * Also \see Add
106 *
107 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
108 * \param begin iterator to futures to Add
109 * \param end iterator to futures to Add
110 */
111 template <bool NeedAdd = true, typename It>
112 YACLIB_INLINE std::enable_if_t<!is_future_base_v<It>, void> Attach(It begin, It end) noexcept {
113 InsertIt<false, NeedAdd>(begin, static_cast<std::size_t>(end - begin));
114 }
115
116 /**
117 * Attach \ref Future to WaitGroup with auto Done
118 *
119 * Also \see Add
120 *
121 * \tparam NeedAdd if true make implicit Add, if false you should make explicit Add before call Attach
122 * \param begin iterator to futures to Add
123 * \param count count of futures to Add
124 */
125 template <bool NeedAdd = true, typename It>
126 YACLIB_INLINE void Attach(It begin, std::size_t count) noexcept {
128 }
129
130 /**
131 * TODO
132 */
134 _event.Wait();
135 }
136 /**
137 * TODO
138 */
139 template <typename Rep, typename Period>
140 YACLIB_INLINE bool WaitFor(const std::chrono::duration<Rep, Period>& timeout_duration) {
141 return _event.WaitFor(timeout_duration);
142 }
143
144 /**
145 * TODO
146 */
147 template <typename Clock, typename Duration>
148 YACLIB_INLINE bool WaitUntil(const std::chrono::time_point<Clock, Duration>& timeout_time) {
149 return _event.WaitUntil(timeout_time);
150 }
151
152#if YACLIB_CORO != 0
153 /**
154 * See OneShotEvent::Await
155 */
157 return _event.Await();
158 }
159
160 /**
161 * See OneShotEvent::Await
162 */
164 return _event.AwaitSticky();
165 }
166
167 /**
168 * See OneShotEvent::AwaitOn
169 */
170 YACLIB_INLINE auto AwaitOn(IExecutor& e) noexcept {
171 return _event.AwaitOn(e);
172 }
173
174 /**
175 * just shortcut for co_await wait_group.Await();
176 *
177 * TODO(MBkkt) move all shortcut to AwaitSticky
178 */
179 YACLIB_INLINE auto operator co_await() noexcept {
180 return Await();
181 }
182#endif
183
184 /**
185 * Reinitializes WaitGroup, semantically the same as `*this = {};`
186 *
187 * If you don't explicitly call this method,
188 * then after the first one, Wait will always return immediately.
189 *
190 * \note Not thread-safe
191 */
192 YACLIB_INLINE void Reset(std::size_t count = 0) noexcept {
193 _event.Reset();
194 _event.count.store(count, std::memory_order_relaxed);
195 }
196
197 private:
198 template <bool NeedMove, bool NeedAdd, typename... Cores>
199 YACLIB_INLINE void InsertCore(Cores&... cores) noexcept {
200 static_assert(sizeof...(cores) >= 1, "Number of futures must be at least one");
201 static_assert((... && std::is_same_v<detail::BaseCore, Cores>),
202 "Futures must be Future in WaitGroup::Consume/Attach function");
203 auto range = [&](auto&& func) noexcept {
204 return (... + static_cast<std::size_t>(func(cores)));
205 };
207 }
208
209 template <bool NeedMove, bool NeedAdd, typename It>
210 YACLIB_INLINE void InsertIt(It it, std::size_t count) noexcept {
212 "WaitGroup::Consume/Attach function Iterator must be point to some Future");
213 if (count == 0) {
214 return;
215 }
216 auto range = [&](auto&& func) noexcept {
217 std::size_t wait_count = 0;
218 for (std::size_t i = 0; i != count; ++i) {
219 if constexpr (NeedMove) {
220 wait_count += static_cast<std::size_t>(func(*it->GetCore().Release()));
221 } else {
222 wait_count += static_cast<std::size_t>(func(*it->GetCore()));
223 }
224 ++it;
225 }
226 return wait_count;
227 };
229 }
230
231 template <bool NeedMove, bool NeedAdd, typename Range>
232 void InsertRange(const Range& range, std::size_t count) noexcept {
233 if constexpr (NeedAdd) {
234 Add(count);
235 }
236 const auto wait_count = range([&](detail::BaseCore& core) noexcept {
237 if constexpr (NeedMove) {
238 if (core.SetCallback(_event.GetDrop())) {
239 return true;
240 }
241 core.DecRef();
242 return false;
243 } else {
244 return core.SetCallback(_event.GetCall());
245 }
246 });
247 if (count != wait_count) { // TODO(MBkkt) is it necessary?
248 Done(count - wait_count);
249 }
250 }
251 detail::MultiEvent<Event, detail::AtomicCounter, detail::CallCallback, detail::DropCallback> _event;
252};
253
254extern template class WaitGroup<OneShotEvent>;
255
256} // namespace yaclib
Provides a mechanism to access the result of async operations.
Definition future.hpp:20
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(FutureBase< V, E > &... 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(FutureBase< V, E > &&... futures) noexcept
Consume Future by WaitGroup with auto Done.
YACLIB_INLINE std::enable_if_t<!is_future_base_v< It >, void > Consume(It begin, It end) 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.
WaitGroup(std::size_t count=0) noexcept
YACLIB_INLINE void Consume(It begin, std::size_t count) noexcept
Consume Future by WaitGroup with auto Done.
YACLIB_INLINE bool WaitUntil(const std::chrono::time_point< Clock, Duration > &timeout_time)
TODO.
YACLIB_INLINE std::enable_if_t<!is_future_base_v< It >, void > Attach(It begin, It end) noexcept
Attach Future to WaitGroup with auto Done.
YACLIB_INLINE auto AwaitOn(IExecutor &e, FutureBase< V, E > &... fs) noexcept
Definition await_on.hpp:11
YACLIB_INLINE auto Await(Task< V, E > &task) noexcept
TODO(mkornaukhov03) Add doxygen docs.
Definition await.hpp:14
Contract< V, E > MakeContract()
Creates related future and promise.
Definition contract.hpp:25