28#include "aria/async/detail/race_slot.hpp"
29#include "aria/async/detail/race_trace.hpp"
51 std::optional<T> value;
52 std::exception_ptr error;
56struct WhenAllSlot<void> {
57 std::exception_ptr error;
73class WhenAllParentHandle {
76 void store(std::coroutine_handle<> h)
noexcept {
77 slot_.store(h.address(), std::memory_order_release);
81 [[nodiscard]] std::coroutine_handle<> load() const noexcept {
82 void* p = slot_.load(std::memory_order_acquire);
83 return p ? std::coroutine_handle<>::from_address(p)
84 : std::coroutine_handle<>{};
88 std::atomic<void*> slot_{
nullptr};
94Task<void> drive_one(Task<T> task,
95 std::shared_ptr<WhenAllSlot<T>> slot,
96 std::shared_ptr<std::atomic<std::size_t>> remaining,
97 std::shared_ptr<WhenAllParentHandle> parent) {
99 if constexpr (std::is_void_v<T>) {
100 co_await std::move(task);
102 slot->value.emplace(
co_await std::move(task));
105 slot->error = std::current_exception();
107 if (remaining->fetch_sub(1, std::memory_order_acq_rel) == 1) {
112 publish_race_trace(race_source::kWhenAll, race_op::kWon);
113 if (
auto h = parent->load()) h.resume();
132template<
typename... Ts>
138 : tasks_(
std::make_tuple(
std::move(ts)...)),
139 slots_(
std::make_tuple(
std::make_shared<detail::WhenAllSlot<Ts>>()...)) {}
144 parent_->store(caller);
145 detail::publish_race_trace(detail::race_source::kWhenAll,
146 detail::race_op::kStart,
151 std::apply([
this](
auto&... task) {
152 std::apply([
this, &task...](
auto&... slot) {
154 std::move(task), slot, remaining_, parent_
155 ).start_detached(), ...);
164 if constexpr (
sizeof...(Ts) > 0) {
165 detail::publish_race_trace(detail::race_source::kWhenAll,
166 detail::race_op::kEnd);
168 std::exception_ptr first_err;
169 std::apply([&](
auto&... slot) {
170 (((slot->error && !first_err) ? first_err = slot->error :
nullptr), ...);
173 if (first_err) std::rethrow_exception(first_err);
174 return build_result_(std::index_sequence_for<Ts...>{});
178 template<std::size_t... I>
179 Result build_result_(std::index_sequence<I...>) {
180 return Result{std::move(*std::get<I>(slots_)->value)...};
183 std::tuple<Task<Ts>...> tasks_;
184 std::tuple<std::shared_ptr<detail::WhenAllSlot<Ts>>...> slots_;
185 std::shared_ptr<std::atomic<std::size_t>> remaining_ =
186 std::make_shared<std::atomic<std::size_t>>(
sizeof...(Ts));
187 std::shared_ptr<detail::WhenAllParentHandle> parent_ =
188 std::make_shared<detail::WhenAllParentHandle>();
191template<
typename... Ts>
205Task<void> drive_any_basic_(Task<T> task,
207 std::shared_ptr<RaceSlot<T>> slot)
210 if constexpr (std::is_void_v<T>) {
211 co_await std::move(task);
212 if (slot->try_claim(1)) {
213 slot->winner_index = idx;
214 slot->store_value_or_exception();
216 publish_race_trace(race_source::kWhenAny, race_op::kWon, idx);
217 slot->notify_winner_resume();
220 T v =
co_await std::move(task);
221 if (slot->try_claim(1)) {
222 slot->winner_index = idx;
223 slot->store_value_or_exception(std::move(v));
225 publish_race_trace(race_source::kWhenAny, race_op::kWon, idx);
226 slot->notify_winner_resume();
230 if (slot->try_claim(1)) {
231 slot->winner_index = idx;
232 slot->result.template emplace<2>(std::current_exception());
236 publish_race_trace(race_source::kWhenAny, race_op::kWon, idx);
237 slot->notify_winner_resume();
246template<
typename T,
typename Factory>
247Task<void> drive_any_cancellable_(Factory factory,
249 std::shared_ptr<RaceSlot<T>> slot,
250 std::shared_ptr<CancellationSource> src,
251 std::shared_ptr<std::vector<std::shared_ptr<CancellationSource>>> all_sources)
253 auto cancel_losers = [all_sources, idx]() {
254 std::uint64_t signalled = 0;
255 for (std::size_t i = 0; i < all_sources->size(); ++i) {
256 if (i == idx)
continue;
257 if (
auto& s = (*all_sources)[i]; s) {
264 publish_race_trace(race_source::kWhenAnyCancellable,
265 race_op::kLoserCancel, signalled);
268 CancellationToken tok = src->token();
271 if constexpr (std::is_void_v<T>) {
272 co_await factory(tok);
273 if (slot->try_claim(1)) {
274 slot->winner_index = idx;
275 slot->store_value_or_exception();
277 publish_race_trace(race_source::kWhenAnyCancellable,
280 slot->notify_winner_resume();
283 T v =
co_await factory(tok);
284 if (slot->try_claim(1)) {
285 slot->winner_index = idx;
286 slot->store_value_or_exception(std::move(v));
288 publish_race_trace(race_source::kWhenAnyCancellable,
291 slot->notify_winner_resume();
295 if (slot->try_claim(1)) {
296 slot->winner_index = idx;
297 slot->result.template emplace<2>(std::current_exception());
299 publish_race_trace(race_source::kWhenAnyCancellable,
302 slot->notify_winner_resume();
331 std::size_t
index = std::size_t(-1);
339 if (tasks_.empty())
return true;
340 return slot_->winner.load(std::memory_order_acquire) != 0;
344 detail::publish_race_trace(detail::race_source::kWhenAny,
345 detail::race_op::kStart,
351 for (std::size_t i = 0; i < tasks_.size(); ++i) {
352 detail::drive_any_basic_<T>(std::move(tasks_[i]), i, slot_)
355 std::lock_guard lk(slot_->mu);
356 if (slot_->winner.load(std::memory_order_acquire) != 0) {
359 slot_->parent_handle = caller;
360 slot_->parent_stored =
true;
367 if (!tasks_.empty()) {
368 detail::publish_race_trace(detail::race_source::kWhenAny,
369 detail::race_op::kEnd);
372 r.
index = slot_->winner_index;
373 auto& v = slot_->result;
374 if (v.index() == 2) {
375 r.
error = std::get<2>(v);
376 }
else if constexpr (!std::is_void_v<T>) {
377 if (v.index() == 1) r.
value = std::get<1>(std::move(v));
383 std::vector<Task<T>> tasks_;
384 std::shared_ptr<detail::RaceSlot<T>> slot_ =
385 std::make_shared<detail::RaceSlot<T>>();
409 : factories_(
std::move(factories)) {
410 sources_->reserve(factories_.size());
411 for (std::size_t i = 0; i < factories_.size(); ++i) {
412 sources_->push_back(std::make_shared<CancellationSource>());
417 if (factories_.empty())
return true;
418 return slot_->winner.load(std::memory_order_acquire) != 0;
422 detail::publish_race_trace(detail::race_source::kWhenAnyCancellable,
423 detail::race_op::kStart,
425 for (std::size_t i = 0; i < factories_.size(); ++i) {
426 detail::drive_any_cancellable_<T, Factory>(
427 std::move(factories_[i]), i, slot_, (*sources_)[i], sources_)
430 std::lock_guard lk(slot_->mu);
431 if (slot_->winner.load(std::memory_order_acquire) != 0) {
434 slot_->parent_handle = caller;
435 slot_->parent_stored =
true;
440 if (!factories_.empty()) {
441 detail::publish_race_trace(detail::race_source::kWhenAnyCancellable,
442 detail::race_op::kEnd);
445 r.index = slot_->winner_index;
446 auto& v = slot_->result;
447 if (v.index() == 2) {
448 r.error = std::get<2>(v);
449 }
else if constexpr (!std::is_void_v<T>) {
450 if (v.index() == 1) r.value = std::get<1>(std::move(v));
456 std::vector<Factory> factories_;
457 std::shared_ptr<detail::RaceSlot<T>> slot_ =
458 std::make_shared<detail::RaceSlot<T>>();
459 std::shared_ptr<std::vector<std::shared_ptr<CancellationSource>>>
460 sources_ = std::make_shared<std::vector<std::shared_ptr<CancellationSource>>>();
Definition cancellation.hpp:182
Awaitable that resolves when ALL input Tasks complete.
Definition when_all.hpp:133
std::tuple< Ts... > Result
Definition when_all.hpp:135
WhenAllAwaiter(Task< Ts >... ts)
Definition when_all.hpp:137
Result await_resume()
Definition when_all.hpp:160
void await_suspend(std::coroutine_handle<> caller)
Definition when_all.hpp:143
bool await_ready() const noexcept
Definition when_all.hpp:141
Awaitable that resolves when ANY of the input Tasks completes (success or error).
Definition when_all.hpp:322
WhenAnyAwaiter(std::vector< Task< T > > tasks)
Definition when_all.hpp:336
bool await_suspend(std::coroutine_handle<> caller) noexcept
Definition when_all.hpp:343
Result await_resume()
Definition when_all.hpp:364
std::conditional_t< std::is_void_v< T >, std::monostate, std::optional< T > > ValueField
Definition when_all.hpp:327
bool await_ready() const noexcept
Definition when_all.hpp:338
Awaitable that resolves when ANY of the input task FACTORIES completes (success or error).
Definition when_all.hpp:403
bool await_suspend(std::coroutine_handle<> caller) noexcept
Definition when_all.hpp:421
bool await_ready() const noexcept
Definition when_all.hpp:416
typename WhenAnyAwaiter< T >::Result Result
Definition when_all.hpp:406
std::function< Task< T >(CancellationToken)> Factory
Definition when_all.hpp:405
Result await_resume()
Definition when_all.hpp:439
WhenAnyCancellableAwaiter(std::vector< Factory > factories)
Definition when_all.hpp:408
Definition async_command.hpp:118
auto when_any(std::vector< Task< T > > tasks)
Definition when_all.hpp:389
auto when_all(Task< Ts >... tasks)
Definition when_all.hpp:192
auto when_any_cancellable(std::vector< std::function< Task< T >(CancellationToken)> > factories)
Definition when_all.hpp:464
Definition validation_key.hpp:110
Definition when_all.hpp:330
std::size_t index
Definition when_all.hpp:331
ValueField value
Definition when_all.hpp:332
std::exception_ptr error
Definition when_all.hpp:333