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>
135 using Result = std::tuple<std::conditional_t<std::is_void_v<Ts>, std::monostate, Ts>...>;
138 : tasks_(
std::make_tuple(
std::move(ts)...)),
139 slots_(
std::make_tuple(
std::make_shared<detail::WhenAllSlot<Ts>>()...)) {}
148 auto drivers = std::apply([
this](
auto&... task) {
149 return std::apply([
this, &task...](
auto&... slot) {
150 return std::make_tuple(detail::drive_one(
151 std::move(task), slot, remaining_, parent_)...);
154 parent_->store(caller);
155 detail::publish_race_trace(detail::race_source::kWhenAll,
156 detail::race_op::kStart,
161 std::apply([](
auto&... driver) {
162 (std::move(driver).start_detached(), ...);
170 if constexpr (
sizeof...(Ts) > 0) {
171 detail::publish_race_trace(detail::race_source::kWhenAll,
172 detail::race_op::kEnd);
174 std::exception_ptr first_err;
175 std::apply([&](
auto&... slot) {
176 (((slot->error && !first_err) ? first_err = slot->error :
nullptr), ...);
179 if (first_err) std::rethrow_exception(first_err);
180 return build_result_(std::index_sequence_for<Ts...>{});
184 template<std::size_t... I>
185 Result build_result_(std::index_sequence<I...>) {
186 return Result{slot_result(std::get<I>(slots_))...};
190 static auto slot_result(
const std::shared_ptr<detail::WhenAllSlot<T>>& slot) {
191 if constexpr (std::is_void_v<T>) {
return std::monostate{}; }
192 else {
return std::move(*slot->value); }
195 std::tuple<Task<Ts>...> tasks_;
196 std::tuple<std::shared_ptr<detail::WhenAllSlot<Ts>>...> slots_;
197 std::shared_ptr<std::atomic<std::size_t>> remaining_ =
198 std::make_shared<std::atomic<std::size_t>>(
sizeof...(Ts));
199 std::shared_ptr<detail::WhenAllParentHandle> parent_ =
200 std::make_shared<detail::WhenAllParentHandle>();
203template<
typename... Ts>
217Task<void> drive_any_basic_(Task<T> task,
219 std::shared_ptr<RaceSlot<T>> slot)
222 if constexpr (std::is_void_v<T>) {
223 co_await std::move(task);
224 if (slot->try_claim(1)) {
225 slot->winner_index = idx;
226 slot->store_value_or_exception();
228 publish_race_trace(race_source::kWhenAny, race_op::kWon, idx);
229 slot->notify_winner_resume();
232 T v =
co_await std::move(task);
233 if (slot->try_claim(1)) {
234 slot->winner_index = idx;
235 slot->store_value_or_exception(std::move(v));
237 publish_race_trace(race_source::kWhenAny, race_op::kWon, idx);
238 slot->notify_winner_resume();
242 if (slot->try_claim(1)) {
243 slot->winner_index = idx;
244 slot->result.template emplace<2>(std::current_exception());
248 publish_race_trace(race_source::kWhenAny, race_op::kWon, idx);
249 slot->notify_winner_resume();
258template<
typename T,
typename Factory>
259Task<void> drive_any_cancellable_(Factory factory,
261 std::shared_ptr<RaceSlot<T>> slot,
262 std::shared_ptr<CancellationSource> src,
263 std::shared_ptr<std::vector<std::shared_ptr<CancellationSource>>> all_sources)
265 auto cancel_losers = [all_sources, idx]() {
266 std::uint64_t signalled = 0;
267 for (std::size_t i = 0; i < all_sources->size(); ++i) {
268 if (i == idx)
continue;
269 if (
auto& s = (*all_sources)[i]; s) {
276 publish_race_trace(race_source::kWhenAnyCancellable,
277 race_op::kLoserCancel, signalled);
280 CancellationToken tok = src->token();
283 if constexpr (std::is_void_v<T>) {
284 co_await factory(tok);
285 if (slot->try_claim(1)) {
286 slot->winner_index = idx;
287 slot->store_value_or_exception();
289 publish_race_trace(race_source::kWhenAnyCancellable,
292 slot->notify_winner_resume();
295 T v =
co_await factory(tok);
296 if (slot->try_claim(1)) {
297 slot->winner_index = idx;
298 slot->store_value_or_exception(std::move(v));
300 publish_race_trace(race_source::kWhenAnyCancellable,
303 slot->notify_winner_resume();
307 if (slot->try_claim(1)) {
308 slot->winner_index = idx;
309 slot->result.template emplace<2>(std::current_exception());
311 publish_race_trace(race_source::kWhenAnyCancellable,
314 slot->notify_winner_resume();
343 std::size_t
index = std::size_t(-1);
351 if (tasks_.empty())
return true;
352 return slot_->winner.load(std::memory_order_acquire) != 0;
356 detail::publish_race_trace(detail::race_source::kWhenAny,
357 detail::race_op::kStart,
363 for (std::size_t i = 0; i < tasks_.size(); ++i) {
364 detail::drive_any_basic_<T>(std::move(tasks_[i]), i, slot_)
367 std::lock_guard lk(slot_->mu);
368 if (slot_->winner.load(std::memory_order_acquire) != 0) {
371 slot_->parent_handle = caller;
372 slot_->parent_stored =
true;
379 if (!tasks_.empty()) {
380 detail::publish_race_trace(detail::race_source::kWhenAny,
381 detail::race_op::kEnd);
384 r.
index = slot_->winner_index;
385 auto& v = slot_->result;
386 if (v.index() == 2) {
387 r.
error = std::get<2>(v);
388 }
else if constexpr (!std::is_void_v<T>) {
389 if (v.index() == 1) r.
value = std::get<1>(std::move(v));
395 std::vector<Task<T>> tasks_;
396 std::shared_ptr<detail::RaceSlot<T>> slot_ =
397 std::make_shared<detail::RaceSlot<T>>();
421 : factories_(
std::move(factories)) {
422 sources_->reserve(factories_.size());
423 for (std::size_t i = 0; i < factories_.size(); ++i) {
424 sources_->push_back(std::make_shared<CancellationSource>());
429 if (factories_.empty())
return true;
430 return slot_->winner.load(std::memory_order_acquire) != 0;
434 detail::publish_race_trace(detail::race_source::kWhenAnyCancellable,
435 detail::race_op::kStart,
437 for (std::size_t i = 0; i < factories_.size(); ++i) {
438 detail::drive_any_cancellable_<T, Factory>(
439 std::move(factories_[i]), i, slot_, (*sources_)[i], sources_)
442 std::lock_guard lk(slot_->mu);
443 if (slot_->winner.load(std::memory_order_acquire) != 0) {
446 slot_->parent_handle = caller;
447 slot_->parent_stored =
true;
452 if (!factories_.empty()) {
453 detail::publish_race_trace(detail::race_source::kWhenAnyCancellable,
454 detail::race_op::kEnd);
457 r.index = slot_->winner_index;
458 auto& v = slot_->result;
459 if (v.index() == 2) {
460 r.error = std::get<2>(v);
461 }
else if constexpr (!std::is_void_v<T>) {
462 if (v.index() == 1) r.value = std::get<1>(std::move(v));
468 std::vector<Factory> factories_;
469 std::shared_ptr<detail::RaceSlot<T>> slot_ =
470 std::make_shared<detail::RaceSlot<T>>();
471 std::shared_ptr<std::vector<std::shared_ptr<CancellationSource>>>
472 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
WhenAllAwaiter(Task< Ts >... ts)
Definition when_all.hpp:137
std::tuple< std::conditional_t< std::is_void_v< Ts >, std::monostate, Ts >... > Result
Definition when_all.hpp:135
Result await_resume()
Definition when_all.hpp:166
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:334
bool await_suspend(std::coroutine_handle<> caller)
Definition when_all.hpp:355
WhenAnyAwaiter(std::vector< Task< T > > tasks)
Definition when_all.hpp:348
Result await_resume()
Definition when_all.hpp:376
std::conditional_t< std::is_void_v< T >, std::monostate, std::optional< T > > ValueField
Definition when_all.hpp:339
bool await_ready() const noexcept
Definition when_all.hpp:350
Awaitable that resolves when ANY of the input task FACTORIES completes (success or error).
Definition when_all.hpp:415
bool await_suspend(std::coroutine_handle<> caller)
Definition when_all.hpp:433
bool await_ready() const noexcept
Definition when_all.hpp:428
typename WhenAnyAwaiter< T >::Result Result
Definition when_all.hpp:418
std::function< Task< T >(CancellationToken)> Factory
Definition when_all.hpp:417
Result await_resume()
Definition when_all.hpp:451
WhenAnyCancellableAwaiter(std::vector< Factory > factories)
Definition when_all.hpp:420
Definition async_command.hpp:118
auto when_any(std::vector< Task< T > > tasks)
Definition when_all.hpp:401
auto when_all(Task< Ts >... tasks)
Definition when_all.hpp:204
auto when_any_cancellable(std::vector< std::function< Task< T >(CancellationToken)> > factories)
Definition when_all.hpp:476
Definition validation_key.hpp:110
Definition when_all.hpp:342
std::size_t index
Definition when_all.hpp:343
ValueField value
Definition when_all.hpp:344
std::exception_ptr error
Definition when_all.hpp:345