100.00% Lines (191/191) 100.00% Functions (43/43)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
3   // Copyright (c) 2026 Steve Gerbino 3   // Copyright (c) 2026 Steve Gerbino
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_WHEN_ANY_HPP 11   #ifndef BOOST_CAPY_WHEN_ANY_HPP
12   #define BOOST_CAPY_WHEN_ANY_HPP 12   #define BOOST_CAPY_WHEN_ANY_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15 - #include <boost/capy/detail/io_result_combinators.hpp> 15 + #include <boost/capy/detail/results_combinators.hpp>
16   #include <boost/capy/continuation.hpp> 16   #include <boost/capy/continuation.hpp>
17   #include <boost/capy/concept/executor.hpp> 17   #include <boost/capy/concept/executor.hpp>
18 - #include <boost/capy/concept/io_awaitable.hpp> 18 + #include <boost/capy/concept/context_awaitable.hpp>
19   #include <coroutine> 19   #include <coroutine>
20   #include <boost/capy/ex/executor_ref.hpp> 20   #include <boost/capy/ex/executor_ref.hpp>
21   #include <boost/capy/detail/abandonable.hpp> 21   #include <boost/capy/detail/abandonable.hpp>
22   #include <boost/capy/detail/owned_range.hpp> 22   #include <boost/capy/detail/owned_range.hpp>
23   #include <boost/capy/ex/abandon.hpp> 23   #include <boost/capy/ex/abandon.hpp>
24   #include <boost/capy/ex/frame_alloc_mixin.hpp> 24   #include <boost/capy/ex/frame_alloc_mixin.hpp>
25   #include <boost/capy/ex/frame_allocator.hpp> 25   #include <boost/capy/ex/frame_allocator.hpp>
26 - #include <boost/capy/ex/io_env.hpp> 26 + #include <boost/capy/ex/await_context.hpp>
27   #include <boost/capy/task.hpp> 27   #include <boost/capy/task.hpp>
28   28  
29   #include <array> 29   #include <array>
30   #include <atomic> 30   #include <atomic>
31   #include <exception> 31   #include <exception>
32   #include <memory> 32   #include <memory>
33   #include <mutex> 33   #include <mutex>
34   #include <optional> 34   #include <optional>
35   #include <ranges> 35   #include <ranges>
36   #include <stdexcept> 36   #include <stdexcept>
37   #include <stop_token> 37   #include <stop_token>
38   #include <tuple> 38   #include <tuple>
39   #include <type_traits> 39   #include <type_traits>
40   #include <utility> 40   #include <utility>
41   #include <variant> 41   #include <variant>
42   #include <vector> 42   #include <vector>
43   43  
44   /* 44   /*
45 - when_any - Race multiple io_result tasks, select first success 45 + when_any - Race multiple results tasks, select first success
46   ============================================================= 46   =============================================================
47   47  
48   OVERVIEW: 48   OVERVIEW:
49   --------- 49   ---------
50 - when_any launches N io_result-returning tasks concurrently. A task 50 + when_any launches N results-returning tasks concurrently. A task
51   wins by returning !ec; errors and exceptions do not win. Once a 51   wins by returning !ec; errors and exceptions do not win. Once a
52   winner is found, stop is requested for siblings and the winner's 52   winner is found, stop is requested for siblings and the winner's
53   payload is returned. If no winner exists (all fail), one of the 53   payload is returned. If no winner exists (all fail), one of the
54   failures is surfaced (an error_code at variant index 0, or a child's 54   failures is surfaced (an error_code at variant index 0, or a child's
55   exception rethrown); which one is unspecified. 55   exception rethrown); which one is unspecified.
56   56  
57   ARCHITECTURE: 57   ARCHITECTURE:
58   ------------- 58   -------------
59   The design mirrors when_all but with inverted completion semantics: 59   The design mirrors when_all but with inverted completion semantics:
60   60  
61   when_all: complete when remaining_count reaches 0 (all done) 61   when_all: complete when remaining_count reaches 0 (all done)
62   when_any: complete when has_winner becomes true (first done) 62   when_any: complete when has_winner becomes true (first done)
63   BUT still wait for remaining_count to reach 0 for cleanup 63   BUT still wait for remaining_count to reach 0 for cleanup
64   64  
65   Key components: 65   Key components:
66   - when_any_core: Shared state tracking winner and completion 66   - when_any_core: Shared state tracking winner and completion
67 - - when_any_io_runner: Wrapper coroutine for each child task 67 + - when_any_results_runner: Wrapper coroutine for each child task
68 - - when_any_io_launcher/when_any_io_homogeneous_launcher: 68 + - when_any_results_launcher/when_any_results_homogeneous_launcher:
69   Awaitables that start all runners concurrently 69   Awaitables that start all runners concurrently
70   70  
71   CRITICAL INVARIANTS: 71   CRITICAL INVARIANTS:
72   -------------------- 72   --------------------
73   1. Only a task returning !ec can become the winner (via atomic CAS) 73   1. Only a task returning !ec can become the winner (via atomic CAS)
74   2. All tasks must complete before parent resumes (cleanup safety) 74   2. All tasks must complete before parent resumes (cleanup safety)
75   3. Stop is requested immediately when winner is determined 75   3. Stop is requested immediately when winner is determined
76   4. Exceptions and errors do not claim winner status 76   4. Exceptions and errors do not claim winner status
77   77  
78   POSITIONAL VARIANT: 78   POSITIONAL VARIANT:
79   ------------------- 79   -------------------
80   The variadic overload returns std::variant<error_code, R1, R2, ..., Rn>. 80   The variadic overload returns std::variant<error_code, R1, R2, ..., Rn>.
81   Index 0 is error_code (failure/no-winner). Index 1..N identifies the 81   Index 0 is error_code (failure/no-winner). Index 1..N identifies the
82   winning child and carries its payload. 82   winning child and carries its payload.
83   83  
84   RANGE OVERLOAD: 84   RANGE OVERLOAD:
85   --------------- 85   ---------------
86   The range overload returns variant<error_code, pair<size_t, T>> for 86   The range overload returns variant<error_code, pair<size_t, T>> for
87   non-void children or variant<error_code, size_t> for void children. 87   non-void children or variant<error_code, size_t> for void children.
88   88  
89   MEMORY MODEL: 89   MEMORY MODEL:
90   ------------- 90   -------------
91   Synchronization chain from winner's write to parent's read: 91   Synchronization chain from winner's write to parent's read:
92   92  
93   1. Winner thread writes result_ (non-atomic) 93   1. Winner thread writes result_ (non-atomic)
94   2. Winner thread calls signal_completion() -> fetch_sub(acq_rel) on remaining_count_ 94   2. Winner thread calls signal_completion() -> fetch_sub(acq_rel) on remaining_count_
95   3. Last task thread (may be winner or non-winner) calls signal_completion() 95   3. Last task thread (may be winner or non-winner) calls signal_completion()
96   -> fetch_sub(acq_rel) on remaining_count_, observing count becomes 0 96   -> fetch_sub(acq_rel) on remaining_count_, observing count becomes 0
97   4. Last task returns caller_ex_.dispatch(continuation_) via symmetric transfer 97   4. Last task returns caller_ex_.dispatch(continuation_) via symmetric transfer
98   5. Parent coroutine resumes and reads result_ 98   5. Parent coroutine resumes and reads result_
99   99  
100   Synchronization analysis: 100   Synchronization analysis:
101   - All fetch_sub operations on remaining_count_ form a release sequence 101   - All fetch_sub operations on remaining_count_ form a release sequence
102   - Winner's fetch_sub releases; subsequent fetch_sub operations participate 102   - Winner's fetch_sub releases; subsequent fetch_sub operations participate
103   in the modification order of remaining_count_ 103   in the modification order of remaining_count_
104   - Last task's fetch_sub(acq_rel) synchronizes-with prior releases in the 104   - Last task's fetch_sub(acq_rel) synchronizes-with prior releases in the
105   modification order, establishing happens-before from winner's writes 105   modification order, establishing happens-before from winner's writes
106   - Executor dispatch() is expected to provide queue-based synchronization 106   - Executor dispatch() is expected to provide queue-based synchronization
107   (release-on-post, acquire-on-execute) completing the chain to parent 107   (release-on-post, acquire-on-execute) completing the chain to parent
108   - Even inline executors work (same thread = sequenced-before) 108   - Even inline executors work (same thread = sequenced-before)
109   109  
110   EXCEPTION SEMANTICS: 110   EXCEPTION SEMANTICS:
111   -------------------- 111   --------------------
112   Exceptions do NOT claim winner status. If a child throws, the exception 112   Exceptions do NOT claim winner status. If a child throws, the exception
113   is recorded but the combinator keeps waiting for a success. Only when 113   is recorded but the combinator keeps waiting for a success. Only when
114   all children complete without a winner is a failure surfaced. There is 114   all children complete without a winner is a failure surfaced. There is
115   no priority between errors and exceptions, and no guarantee about which 115   no priority between errors and exceptions, and no guarantee about which
116   child's failure is reported: the result either returns an error_code at 116   child's failure is reported: the result either returns an error_code at
117   variant index 0 or rethrows a child's exception. 117   variant index 0 or rethrows a child's exception.
118   */ 118   */
119   119  
120   namespace boost { 120   namespace boost {
121   namespace capy { 121   namespace capy {
122   122  
123   namespace detail { 123   namespace detail {
124   124  
125   /** Core shared state for when_any operations. 125   /** Core shared state for when_any operations.
126   126  
127   Contains all members and methods common to both heterogeneous (variadic) 127   Contains all members and methods common to both heterogeneous (variadic)
128   and homogeneous (range) when_any implementations. State classes embed 128   and homogeneous (range) when_any implementations. State classes embed
129   this via composition to avoid CRTP destructor ordering issues. 129   this via composition to avoid CRTP destructor ordering issues.
130   130  
131   @par Thread Safety 131   @par Thread Safety
132   Atomic operations protect winner selection and completion count. 132   Atomic operations protect winner selection and completion count.
133   */ 133   */
134   struct when_any_core 134   struct when_any_core
135   { 135   {
136   std::atomic<std::size_t> remaining_count_; 136   std::atomic<std::size_t> remaining_count_;
137   std::size_t winner_index_{0}; 137   std::size_t winner_index_{0};
138   std::exception_ptr winner_exception_; 138   std::exception_ptr winner_exception_;
139   std::stop_source stop_source_; 139   std::stop_source stop_source_;
140   140  
141   // Bridges parent's stop token to our stop_source 141   // Bridges parent's stop token to our stop_source
142   struct stop_callback_fn 142   struct stop_callback_fn
143   { 143   {
144   std::stop_source* source_; 144   std::stop_source* source_;
HITCBC 145   4 void operator()() const noexcept 145   4 void operator()() const noexcept
146   { 146   {
147   // Stopping can lose the last branch and destroy the join, 147   // Stopping can lose the last branch and destroy the join,
148   // with its source, before the request returns; the copy 148   // with its source, before the request returns; the copy
149   // keeps the stop state alive until then. 149   // keeps the stop state alive until then.
HITCBC 150   4 std::stop_source keep = *source_; 150   4 std::stop_source keep = *source_;
HITCBC 151   4 keep.request_stop(); 151   4 keep.request_stop();
HITCBC 152   4 } 152   4 }
153   }; 153   };
154   using stop_callback_t = std::stop_callback<stop_callback_fn>; 154   using stop_callback_t = std::stop_callback<stop_callback_fn>;
155   std::optional<stop_callback_t> parent_stop_callback_; 155   std::optional<stop_callback_t> parent_stop_callback_;
156   156  
157   continuation continuation_; 157   continuation continuation_;
158 - io_env const* caller_env_ = nullptr; 158 + await_context const* caller_env_ = nullptr;
159   159  
160   // Set when a branch is abandoned before finishing: the parent is 160   // Set when a branch is abandoned before finishing: the parent is
161   // then abandoned rather than resumed. 161   // then abandoned rather than resumed.
162   std::atomic<bool> lost_{false}; 162   std::atomic<bool> lost_{false};
163   163  
164   /// Drop a share held by a runner that never finished. 164   /// Drop a share held by a runner that never finished.
HITCBC 165   8 void lose_branch() noexcept 165   8 void lose_branch() noexcept
166   { 166   {
HITCBC 167   8 lost_.store(true, std::memory_order_relaxed); 167   8 lost_.store(true, std::memory_order_relaxed);
HITCBC 168   8 stop_source_.request_stop(); 168   8 stop_source_.request_stop();
HITCBC 169   16 if(remaining_count_.fetch_sub(1, std::memory_order_acq_rel) == 1) 169   16 if(remaining_count_.fetch_sub(1, std::memory_order_acq_rel) == 1)
170   { 170   {
HITCBC 171   4 auto parent = continuation_; 171   4 auto parent = continuation_;
HITCBC 172   4 capy::abandon(parent); 172   4 capy::abandon(parent);
173   } 173   }
HITCBC 174   8 } 174   8 }
175   175  
176   // Placed last to avoid padding (1-byte atomic followed by 8-byte aligned members) 176   // Placed last to avoid padding (1-byte atomic followed by 8-byte aligned members)
177   std::atomic<bool> has_winner_{false}; 177   std::atomic<bool> has_winner_{false};
178   178  
HITCBC 179   53 explicit when_any_core(std::size_t count) noexcept 179   53 explicit when_any_core(std::size_t count) noexcept
HITCBC 180   53 : remaining_count_(count) 180   53 : remaining_count_(count)
181   { 181   {
HITCBC 182   53 } 182   53 }
183   183  
184   /** Atomically claim winner status; exactly one task succeeds. */ 184   /** Atomically claim winner status; exactly one task succeeds. */
HITCBC 185   68 bool try_win(std::size_t index) noexcept 185   68 bool try_win(std::size_t index) noexcept
186   { 186   {
HITCBC 187   68 bool expected = false; 187   68 bool expected = false;
HITCBC 188   68 if(has_winner_.compare_exchange_strong( 188   68 if(has_winner_.compare_exchange_strong(
189   expected, true, std::memory_order_acq_rel)) 189   expected, true, std::memory_order_acq_rel))
190   { 190   {
HITCBC 191   32 winner_index_ = index; 191   32 winner_index_ = index;
HITCBC 192   32 stop_source_.request_stop(); 192   32 stop_source_.request_stop();
HITCBC 193   32 return true; 193   32 return true;
194   } 194   }
HITCBC 195   36 return false; 195   36 return false;
196   } 196   }
197   197  
198   /** @pre try_win() returned true. */ 198   /** @pre try_win() returned true. */
HITCBC 199   1 void set_winner_exception(std::exception_ptr ep) noexcept 199   1 void set_winner_exception(std::exception_ptr ep) noexcept
200   { 200   {
HITCBC 201   1 winner_exception_ = ep; 201   1 winner_exception_ = ep;
HITCBC 202   1 } 202   1 }
203   203  
204   // Runners signal completion directly via final_suspend; no member function needed. 204   // Runners signal completion directly via final_suspend; no member function needed.
205   }; 205   };
206   206  
207   } // namespace detail 207   } // namespace detail
208   208  
209   namespace detail { 209   namespace detail {
210   210  
211 - // State for io_result-aware when_any: only !ec wins. 211 + // State for `results`-aware when_any: only !ec wins.
212   template<typename... Ts> 212   template<typename... Ts>
213 - struct when_any_io_state 213 + struct when_any_results_state
214   { 214   {
215   static constexpr std::size_t task_count = sizeof...(Ts); 215   static constexpr std::size_t task_count = sizeof...(Ts);
216   using variant_type = std::variant<std::error_code, Ts...>; 216   using variant_type = std::variant<std::error_code, Ts...>;
217   217  
218   when_any_core core_; 218   when_any_core core_;
219   std::optional<variant_type> result_; 219   std::optional<variant_type> result_;
220   std::array<continuation, task_count> runner_handles_{}; 220   std::array<continuation, task_count> runner_handles_{};
221   221  
222   // A failure (error or exception) for the all-fail case. record_error 222   // A failure (error or exception) for the all-fail case. record_error
223   // and record_exception overwrite each other, so which one survives is 223   // and record_exception overwrite each other, so which one survives is
224   // unspecified (no priority between errors and exceptions). 224   // unspecified (no priority between errors and exceptions).
225   std::mutex failure_mu_; 225   std::mutex failure_mu_;
226   std::error_code last_error_; 226   std::error_code last_error_;
227   std::exception_ptr last_exception_; 227   std::exception_ptr last_exception_;
228   228  
HITCBC 229 - 33 when_any_io_state() 229 + 33 when_any_results_state()
HITCBC 230   33 : core_(task_count) 230   33 : core_(task_count)
231   { 231   {
HITCBC 232   33 } 232   33 }
233   233  
HITCBC 234   16 void record_error(std::error_code ec) 234   16 void record_error(std::error_code ec)
235   { 235   {
HITCBC 236   16 std::lock_guard lk(failure_mu_); 236   16 std::lock_guard lk(failure_mu_);
HITCBC 237   16 last_error_ = ec; 237   16 last_error_ = ec;
HITCBC 238   16 last_exception_ = nullptr; 238   16 last_exception_ = nullptr;
HITCBC 239   16 } 239   16 }
240   240  
HITCBC 241   7 void record_exception(std::exception_ptr ep) 241   7 void record_exception(std::exception_ptr ep)
242   { 242   {
HITCBC 243   7 std::lock_guard lk(failure_mu_); 243   7 std::lock_guard lk(failure_mu_);
HITCBC 244   7 last_exception_ = ep; 244   7 last_exception_ = ep;
HITCBC 245   7 last_error_ = {}; 245   7 last_error_ = {};
HITCBC 246   7 } 246   7 }
247   }; 247   };
248   248  
249 - // Wrapper coroutine for io_result-aware when_any children. 249 + // Wrapper coroutine for `results`-aware when_any children.
250   // unhandled_exception records the exception but does NOT claim winner status. 250   // unhandled_exception records the exception but does NOT claim winner status.
251   template<typename StateType> 251   template<typename StateType>
252 - struct when_any_io_runner 252 + struct when_any_results_runner
253   { 253   {
254   struct promise_type 254   struct promise_type
255   : detail::process_frame_mixin 255   : detail::process_frame_mixin
256   { 256   {
257   StateType* state_ = nullptr; 257   StateType* state_ = nullptr;
258   std::size_t index_ = 0; 258   std::size_t index_ = 0;
259 - io_env env_; 259 + await_context env_;
260   // Owns the branch's segment. 260   // Owns the branch's segment.
261   chain_owner segment_; 261   chain_owner segment_;
262   bool done_ = false; 262   bool done_ = false;
263   263  
HITCBC 264   126 ~promise_type() 264   126 ~promise_type()
265   { 265   {
HITCBC 266   126 if(!done_ && state_) 266   126 if(!done_ && state_)
HITCBC 267   8 state_->core_.lose_branch(); 267   8 state_->core_.lose_branch();
HITCBC 268   126 } 268   126 }
269   269  
HITCBC 270 - 126 when_any_io_runner get_return_object() noexcept 270 + 126 when_any_results_runner get_return_object() noexcept
271   { 271   {
272 - return when_any_io_runner( 272 + return when_any_results_runner(
HITCBC 273   126 std::coroutine_handle<promise_type>::from_promise(*this)); 273   126 std::coroutine_handle<promise_type>::from_promise(*this));
274   } 274   }
275   275  
HITCBC 276   126 std::suspend_always initial_suspend() noexcept { return {}; } 276   126 std::suspend_always initial_suspend() noexcept { return {}; }
277   277  
HITCBC 278   118 auto final_suspend() noexcept 278   118 auto final_suspend() noexcept
279   { 279   {
280   struct awaiter 280   struct awaiter
281   { 281   {
282   promise_type* p_; 282   promise_type* p_;
HITCBC 283   118 bool await_ready() const noexcept { return false; } 283   118 bool await_ready() const noexcept { return false; }
HITCBC 284   118 auto await_suspend(std::coroutine_handle<> h) noexcept 284   118 auto await_suspend(std::coroutine_handle<> h) noexcept
285   { 285   {
HITCBC 286   118 auto& core = p_->state_->core_; 286   118 auto& core = p_->state_->core_;
HITCBC 287   118 auto* counter = &core.remaining_count_; 287   118 auto* counter = &core.remaining_count_;
HITCBC 288   118 auto* lost = &core.lost_; 288   118 auto* lost = &core.lost_;
HITCBC 289   118 auto* caller_env = core.caller_env_; 289   118 auto* caller_env = core.caller_env_;
HITCBC 290   118 auto& cont = core.continuation_; 290   118 auto& cont = core.continuation_;
291   291  
HITCBC 292   118 p_->done_ = true; 292   118 p_->done_ = true;
293   // Resumed to unwind its abandoned branch: lost, not 293   // Resumed to unwind its abandoned branch: lost, not
294   // finished. Stop is requested while this branch 294   // finished. Stop is requested while this branch
295   // still holds its share. 295   // still holds its share.
HITCBC 296   118 if(p_->segment_.abandoned()) 296   118 if(p_->segment_.abandoned())
297   { 297   {
HITCBC 298   14 lost->store(true, std::memory_order_relaxed); 298   14 lost->store(true, std::memory_order_relaxed);
HITCBC 299   14 core.stop_source_.request_stop(); 299   14 core.stop_source_.request_stop();
300   } 300   }
HITCBC 301   118 h.destroy(); 301   118 h.destroy();
302   302  
HITCBC 303   118 auto remaining = counter->fetch_sub(1, std::memory_order_acq_rel); 303   118 auto remaining = counter->fetch_sub(1, std::memory_order_acq_rel);
HITCBC 304   118 if(remaining != 1) 304   118 if(remaining != 1)
HITCBC 305   69 return detail::symmetric_transfer(std::noop_coroutine()); 305   69 return detail::symmetric_transfer(std::noop_coroutine());
HITCBC 306   49 if(lost->load(std::memory_order_relaxed)) 306   49 if(lost->load(std::memory_order_relaxed))
307   { 307   {
HITCBC 308   8 auto parent = cont; 308   8 auto parent = cont;
HITCBC 309   8 capy::abandon(parent); 309   8 capy::abandon(parent);
HITCBC 310   8 return detail::symmetric_transfer(std::noop_coroutine()); 310   8 return detail::symmetric_transfer(std::noop_coroutine());
311   } 311   }
HITCBC 312   41 return detail::symmetric_transfer(caller_env->executor.dispatch(cont)); 312   41 return detail::symmetric_transfer(caller_env->executor.dispatch(cont));
313   } 313   }
314   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed 314   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed
315   }; 315   };
HITCBC 316   118 return awaiter{this}; 316   118 return awaiter{this};
317   } 317   }
318   318  
HITCBC 319   105 void return_void() noexcept {} 319   105 void return_void() noexcept {}
320   320  
321 - // Exceptions do NOT win in io_result when_any 321 + // Exceptions do NOT win in `results` when_any
HITCBC 322   13 void unhandled_exception() noexcept 322   13 void unhandled_exception() noexcept
323   { 323   {
HITCBC 324   13 state_->record_exception(std::current_exception()); 324   13 state_->record_exception(std::current_exception());
HITCBC 325   13 } 325   13 }
326   326  
327   template<class Awaitable> 327   template<class Awaitable>
328   struct transform_awaiter 328   struct transform_awaiter
329   { 329   {
330   std::decay_t<Awaitable> a_; 330   std::decay_t<Awaitable> a_;
331   promise_type* p_; 331   promise_type* p_;
332   332  
HITCBC 333   120 bool await_ready() { return a_.await_ready(); } 333   120 bool await_ready() { return a_.await_ready(); }
HITCBC 334   118 decltype(auto) await_resume() { return a_.await_resume(); } 334   118 decltype(auto) await_resume() { return a_.await_resume(); }
335   335  
336   template<class Promise> 336   template<class Promise>
HITCBC 337   119 auto await_suspend(std::coroutine_handle<Promise> h) 337   119 auto await_suspend(std::coroutine_handle<Promise> h)
338   { 338   {
339   using R = decltype(a_.await_suspend(h, &p_->env_)); 339   using R = decltype(a_.await_suspend(h, &p_->env_));
340   if constexpr (std::is_same_v<R, std::coroutine_handle<>>) 340   if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
HITCBC 341   119 return detail::symmetric_transfer(a_.await_suspend(h, &p_->env_)); 341   119 return detail::symmetric_transfer(a_.await_suspend(h, &p_->env_));
342   else 342   else
343   return a_.await_suspend(h, &p_->env_); 343   return a_.await_suspend(h, &p_->env_);
344   } 344   }
345   }; 345   };
346   346  
347   template<class Awaitable> 347   template<class Awaitable>
HITCBC 348   120 auto await_transform(Awaitable&& a) 348   120 auto await_transform(Awaitable&& a)
349   { 349   {
350   using A = std::decay_t<Awaitable>; 350   using A = std::decay_t<Awaitable>;
351 - if constexpr (IoAwaitable<A>) 351 + if constexpr (ContextAwaitable<A>)
352   { 352   {
353   return transform_awaiter<Awaitable>{ 353   return transform_awaiter<Awaitable>{
HITCBC 354   120 std::forward<Awaitable>(a), this}; 354   120 std::forward<Awaitable>(a), this};
355   } 355   }
356   else 356   else
357   { 357   {
358 - static_assert(sizeof(A) == 0, "requires IoAwaitable"); 358 + static_assert(sizeof(A) == 0, "requires ContextAwaitable");
359   } 359   }
360   } 360   }
361   }; 361   };
362   362  
363   std::coroutine_handle<promise_type> h_; 363   std::coroutine_handle<promise_type> h_;
364   364  
HITCBC 365 - 126 explicit when_any_io_runner(std::coroutine_handle<promise_type> h) noexcept 365 + 126 explicit when_any_results_runner(std::coroutine_handle<promise_type> h) noexcept
HITCBC 366   126 : h_(h) 366   126 : h_(h)
367   { 367   {
HITCBC 368   126 } 368   126 }
369   369  
370 - when_any_io_runner(when_any_io_runner&& other) noexcept 370 + when_any_results_runner(when_any_results_runner&& other) noexcept
371   : h_(std::exchange(other.h_, nullptr)) 371   : h_(std::exchange(other.h_, nullptr))
372   { 372   {
373   } 373   }
374   374  
375 - when_any_io_runner(when_any_io_runner const&) = delete; 375 + when_any_results_runner(when_any_results_runner const&) = delete;
376 - when_any_io_runner& operator=(when_any_io_runner const&) = delete; 376 + when_any_results_runner& operator=(when_any_results_runner const&) = delete;
377 - when_any_io_runner& operator=(when_any_io_runner&&) = delete; 377 + when_any_results_runner& operator=(when_any_results_runner&&) = delete;
378   378  
HITCBC 379   126 auto release() noexcept 379   126 auto release() noexcept
380   { 380   {
HITCBC 381   126 return std::exchange(h_, nullptr); 381   126 return std::exchange(h_, nullptr);
382   } 382   }
383   }; 383   };
384   384  
385   // Runner coroutine: only tries to win when the child returns !ec. 385   // Runner coroutine: only tries to win when the child returns !ec.
386 - template<std::size_t I, IoAwaitable Awaitable, typename StateType> 386 + template<std::size_t I, ContextAwaitable Awaitable, typename StateType>
387 - when_any_io_runner<StateType> 387 + when_any_results_runner<StateType>
HITCBC 388 - 61 make_when_any_io_runner(Awaitable* inner, StateType* state) 388 + 61 make_when_any_results_runner(Awaitable* inner, StateType* state)
389   { 389   {
390   // Taken from the join's storage only once running, into a local: 390   // Taken from the join's storage only once running, into a local:
391   // a runner destroyed unstarted owns no branch, and one destroyed 391   // a runner destroyed unstarted owns no branch, and one destroyed
392   // unfinished destroys it before its promise reports the loss. 392   // unfinished destroys it before its promise reports the loss.
393   Awaitable a(std::move(*inner)); 393   Awaitable a(std::move(*inner));
394   auto r = co_await abandonable<Awaitable>{a}; 394   auto r = co_await abandonable<Awaitable>{a};
395   if(!r) 395   if(!r)
396   co_return; 396   co_return;
397   auto& result = *r; 397   auto& result = *r;
398   398  
399   if(!std::get<0>(result)) 399   if(!std::get<0>(result))
400   { 400   {
401   // Success: try to claim winner 401   // Success: try to claim winner
402   if(state->core_.try_win(I)) 402   if(state->core_.try_win(I))
403   { 403   {
404   try 404   try
405   { 405   {
406   state->result_.emplace( 406   state->result_.emplace(
407   std::in_place_index<I + 1>, 407   std::in_place_index<I + 1>,
408 - detail::extract_io_payload(std::move(result))); 408 + detail::extract_results_payload(std::move(result)));
409   } 409   }
410   catch(...) 410   catch(...)
411   { 411   {
412   state->core_.set_winner_exception(std::current_exception()); 412   state->core_.set_winner_exception(std::current_exception());
413   } 413   }
414   } 414   }
415   } 415   }
416   else 416   else
417   { 417   {
418   // Error: record but don't win 418   // Error: record but don't win
419   state->record_error(std::get<0>(result)); 419   state->record_error(std::get<0>(result));
420   } 420   }
HITCBC 421   122 } 421   122 }
422   422  
423 - // Launcher for io_result-aware when_any. 423 + // Launcher for `results`-aware when_any.
424 - template<IoAwaitable... Awaitables> 424 + template<ContextAwaitable... Awaitables>
425 - class when_any_io_launcher 425 + class when_any_results_launcher
426   { 426   {
427 - using state_type = when_any_io_state< 427 + using state_type = when_any_results_state<
428 - io_result_payload_t<awaitable_result_t<Awaitables>>...>; 428 + results_payload_t<awaitable_result_t<Awaitables>>...>;
429   429  
430   std::tuple<Awaitables...>* tasks_; 430   std::tuple<Awaitables...>* tasks_;
431   state_type* state_; 431   state_type* state_;
432   432  
433   public: 433   public:
HITCBC 434 - 33 when_any_io_launcher( 434 + 33 when_any_results_launcher(
435   std::tuple<Awaitables...>* tasks, 435   std::tuple<Awaitables...>* tasks,
436   state_type* state) 436   state_type* state)
HITCBC 437   33 : tasks_(tasks) 437   33 : tasks_(tasks)
HITCBC 438   33 , state_(state) 438   33 , state_(state)
439   { 439   {
HITCBC 440   33 } 440   33 }
441   441  
HITCBC 442   33 bool await_ready() const noexcept 442   33 bool await_ready() const noexcept
443   { 443   {
HITCBC 444   33 return sizeof...(Awaitables) == 0; 444   33 return sizeof...(Awaitables) == 0;
445   } 445   }
446   446  
HITCBC 447   33 std::coroutine_handle<> await_suspend( 447   33 std::coroutine_handle<> await_suspend(
448 - std::coroutine_handle<> continuation, io_env const* caller_env) 448 + std::coroutine_handle<> continuation, await_context const* caller_env)
449   { 449   {
HITCBC 450   33 state_->core_.continuation_.h = continuation; 450   33 state_->core_.continuation_.h = continuation;
HITCBC 451   33 state_->core_.continuation_.owner = caller_env->owner; 451   33 state_->core_.continuation_.owner = caller_env->owner;
HITCBC 452   33 state_->core_.caller_env_ = caller_env; 452   33 state_->core_.caller_env_ = caller_env;
453   453  
HITCBC 454   33 if(caller_env->stop_token.stop_possible()) 454   33 if(caller_env->stop_token.stop_possible())
455   { 455   {
HITCBC 456   6 state_->core_.parent_stop_callback_.emplace( 456   6 state_->core_.parent_stop_callback_.emplace(
HITCBC 457   3 caller_env->stop_token, 457   3 caller_env->stop_token,
HITCBC 458   3 when_any_core::stop_callback_fn{&state_->core_.stop_source_}); 458   3 when_any_core::stop_callback_fn{&state_->core_.stop_source_});
459   459  
HITCBC 460   3 if(caller_env->stop_token.stop_requested()) 460   3 if(caller_env->stop_token.stop_requested())
HITCBC 461   1 state_->core_.stop_source_.request_stop(); 461   1 state_->core_.stop_source_.request_stop();
462   } 462   }
463   463  
HITCBC 464   33 auto token = state_->core_.stop_source_.get_token(); 464   33 auto token = state_->core_.stop_source_.get_token();
HITCBC 465   33 launch_all(std::index_sequence_for<Awaitables...>{}, 465   33 launch_all(std::index_sequence_for<Awaitables...>{},
466   caller_env->executor, token); 466   caller_env->executor, token);
467   467  
HITCBC 468   66 return std::noop_coroutine(); 468   66 return std::noop_coroutine();
HITCBC 469   33 } 469   33 }
470   470  
HITCBC 471   23 void await_resume() const noexcept {} 471   23 void await_resume() const noexcept {}
472   472  
473   private: 473   private:
474   template<std::size_t... Is> 474   template<std::size_t... Is>
HITCBC 475   33 void launch_all(std::index_sequence<Is...>, 475   33 void launch_all(std::index_sequence<Is...>,
476   executor_ref ex, std::stop_token token) 476   executor_ref ex, std::stop_token token)
477   { 477   {
HITCBC 478   33 (..., launch_one<Is>(ex, token)); 478   33 (..., launch_one<Is>(ex, token));
HITCBC 479   33 } 479   33 }
480   480  
481   template<std::size_t I> 481   template<std::size_t I>
HITCBC 482   61 void launch_one(executor_ref caller_ex, std::stop_token token) 482   61 void launch_one(executor_ref caller_ex, std::stop_token token)
483   { 483   {
HITCBC 484 - 61 auto runner = make_when_any_io_runner<I>( 484 + 61 auto runner = make_when_any_results_runner<I>(
HITCBC 485   61 &std::get<I>(*tasks_), state_); 485   61 &std::get<I>(*tasks_), state_);
486   486  
HITCBC 487   61 auto h = runner.release(); 487   61 auto h = runner.release();
HITCBC 488   61 h.promise().state_ = state_; 488   61 h.promise().state_ = state_;
HITCBC 489   61 h.promise().index_ = I; 489   61 h.promise().index_ = I;
HITCBC 490   61 h.promise().segment_.bind(h, caller_ex); 490   61 h.promise().segment_.bind(h, caller_ex);
HITCBC 491 - 61 h.promise().env_ = io_env{caller_ex, token, 491 + 61 h.promise().env_ = await_context{caller_ex, token,
HITCBC 492   61 state_->core_.caller_env_->frame_allocator, 492   61 state_->core_.caller_env_->frame_allocator,
HITCBC 493   122 &h.promise().segment_}; 493   122 &h.promise().segment_};
494   494  
HITCBC 495   61 state_->runner_handles_[I].h = std::coroutine_handle<>{h}; 495   61 state_->runner_handles_[I].h = std::coroutine_handle<>{h};
HITCBC 496   61 caller_ex.post(state_->runner_handles_[I]); 496   61 caller_ex.post(state_->runner_handles_[I]);
HITCBC 497   122 } 497   122 }
498   }; 498   };
499   499  
500 - /** Shared state for homogeneous io_result-aware when_any (range overload). 500 + /** Shared state for homogeneous `results`-aware when_any (range overload).
501   501  
502 - @tparam T The payload type extracted from io_result. 502 + @tparam T The payload type extracted from `results`.
503   */ 503   */
504   template<typename T> 504   template<typename T>
505 - struct when_any_io_homogeneous_state 505 + struct when_any_results_homogeneous_state
506   { 506   {
507   when_any_core core_; 507   when_any_core core_;
508   std::optional<T> result_; 508   std::optional<T> result_;
509   std::unique_ptr<continuation[]> runner_handles_; 509   std::unique_ptr<continuation[]> runner_handles_;
510   510  
511   std::mutex failure_mu_; 511   std::mutex failure_mu_;
512   std::error_code last_error_; 512   std::error_code last_error_;
513   std::exception_ptr last_exception_; 513   std::exception_ptr last_exception_;
514   514  
HITCBC 515 - 15 explicit when_any_io_homogeneous_state(std::size_t count) 515 + 15 explicit when_any_results_homogeneous_state(std::size_t count)
HITCBC 516   15 : core_(count) 516   15 : core_(count)
HITCBC 517   15 , runner_handles_(std::make_unique<continuation[]>(count)) 517   15 , runner_handles_(std::make_unique<continuation[]>(count))
518   { 518   {
HITCBC 519   15 } 519   15 }
520   520  
HITCBC 521   6 void record_error(std::error_code ec) 521   6 void record_error(std::error_code ec)
522   { 522   {
HITCBC 523   6 std::lock_guard lk(failure_mu_); 523   6 std::lock_guard lk(failure_mu_);
HITCBC 524   6 last_error_ = ec; 524   6 last_error_ = ec;
HITCBC 525   6 last_exception_ = nullptr; 525   6 last_exception_ = nullptr;
HITCBC 526   6 } 526   6 }
527   527  
HITCBC 528   4 void record_exception(std::exception_ptr ep) 528   4 void record_exception(std::exception_ptr ep)
529   { 529   {
HITCBC 530   4 std::lock_guard lk(failure_mu_); 530   4 std::lock_guard lk(failure_mu_);
HITCBC 531   4 last_exception_ = ep; 531   4 last_exception_ = ep;
HITCBC 532   4 last_error_ = {}; 532   4 last_error_ = {};
HITCBC 533   4 } 533   4 }
534   }; 534   };
535   535  
536 - /** Specialization for void io_result children (no payload storage). */ 536 + /** Specialization for void `results` children (no payload storage). */
537   template<> 537   template<>
538 - struct when_any_io_homogeneous_state<std::tuple<>> 538 + struct when_any_results_homogeneous_state<std::tuple<>>
539   { 539   {
540   when_any_core core_; 540   when_any_core core_;
541   std::unique_ptr<continuation[]> runner_handles_; 541   std::unique_ptr<continuation[]> runner_handles_;
542   542  
543   std::mutex failure_mu_; 543   std::mutex failure_mu_;
544   std::error_code last_error_; 544   std::error_code last_error_;
545   std::exception_ptr last_exception_; 545   std::exception_ptr last_exception_;
546   546  
HITCBC 547 - 5 explicit when_any_io_homogeneous_state(std::size_t count) 547 + 5 explicit when_any_results_homogeneous_state(std::size_t count)
HITCBC 548   5 : core_(count) 548   5 : core_(count)
HITCBC 549   5 , runner_handles_(std::make_unique<continuation[]>(count)) 549   5 , runner_handles_(std::make_unique<continuation[]>(count))
550   { 550   {
HITCBC 551   5 } 551   5 }
552   552  
HITCBC 553   1 void record_error(std::error_code ec) 553   1 void record_error(std::error_code ec)
554   { 554   {
HITCBC 555   1 std::lock_guard lk(failure_mu_); 555   1 std::lock_guard lk(failure_mu_);
HITCBC 556   1 last_error_ = ec; 556   1 last_error_ = ec;
HITCBC 557   1 last_exception_ = nullptr; 557   1 last_exception_ = nullptr;
HITCBC 558   1 } 558   1 }
559   559  
HITCBC 560   2 void record_exception(std::exception_ptr ep) 560   2 void record_exception(std::exception_ptr ep)
561   { 561   {
HITCBC 562   2 std::lock_guard lk(failure_mu_); 562   2 std::lock_guard lk(failure_mu_);
HITCBC 563   2 last_exception_ = ep; 563   2 last_exception_ = ep;
HITCBC 564   2 last_error_ = {}; 564   2 last_error_ = {};
HITCBC 565   2 } 565   2 }
566   }; 566   };
567   567  
568 - /** Create an io_result-aware runner for homogeneous when_any (range path). 568 + /** Create a `results`-aware runner for homogeneous when_any (range path).
569   569  
570   Only tries to win when the child returns !ec. 570   Only tries to win when the child returns !ec.
571   */ 571   */
572 - template<IoAwaitable Awaitable, typename StateType> 572 + template<ContextAwaitable Awaitable, typename StateType>
573 - when_any_io_runner<StateType> 573 + when_any_results_runner<StateType>
HITCBC 574 - 65 make_when_any_io_homogeneous_runner( 574 + 65 make_when_any_results_homogeneous_runner(
575   Awaitable* inner, StateType* state, std::size_t index) 575   Awaitable* inner, StateType* state, std::size_t index)
576   { 576   {
577   // Taken from the join's storage only once running, into a local: 577   // Taken from the join's storage only once running, into a local:
578   // a runner destroyed unstarted owns no branch, and one destroyed 578   // a runner destroyed unstarted owns no branch, and one destroyed
579   // unfinished destroys it before its promise reports the loss. 579   // unfinished destroys it before its promise reports the loss.
580   Awaitable a(std::move(*inner)); 580   Awaitable a(std::move(*inner));
581   auto r = co_await abandonable<Awaitable>{a}; 581   auto r = co_await abandonable<Awaitable>{a};
582   if(!r) 582   if(!r)
583   co_return; 583   co_return;
584   auto& result = *r; 584   auto& result = *r;
585   585  
586   if(!std::get<0>(result)) 586   if(!std::get<0>(result))
587   { 587   {
588   if(state->core_.try_win(index)) 588   if(state->core_.try_win(index))
589   { 589   {
590 - using PayloadT = io_result_payload_t< 590 + using PayloadT = results_payload_t<
591   awaitable_result_t<Awaitable>>; 591   awaitable_result_t<Awaitable>>;
592   if constexpr (!std::is_same_v<PayloadT, std::tuple<>>) 592   if constexpr (!std::is_same_v<PayloadT, std::tuple<>>)
593   { 593   {
594   try 594   try
595   { 595   {
596   state->result_.emplace( 596   state->result_.emplace(
597 - extract_io_payload(std::move(result))); 597 + extract_results_payload(std::move(result)));
598   } 598   }
599   catch(...) 599   catch(...)
600   { 600   {
601   state->core_.set_winner_exception( 601   state->core_.set_winner_exception(
602   std::current_exception()); 602   std::current_exception());
603   } 603   }
604   } 604   }
605   } 605   }
606   } 606   }
607   else 607   else
608   { 608   {
609   state->record_error(std::get<0>(result)); 609   state->record_error(std::get<0>(result));
610   } 610   }
HITCBC 611   130 } 611   130 }
612   612  
613 - /** Starts all io_result-aware homogeneous runners concurrently. */ 613 + /** Starts all `results`-aware homogeneous runners concurrently. */
614 - template<IoAwaitableRange Range> 614 + template<ContextAwaitableRange Range>
615 - class when_any_io_homogeneous_launcher 615 + class when_any_results_homogeneous_launcher
616   { 616   {
617   using Awaitable = std::ranges::range_value_t<Range>; 617   using Awaitable = std::ranges::range_value_t<Range>;
618 - using PayloadT = io_result_payload_t<awaitable_result_t<Awaitable>>; 618 + using PayloadT = results_payload_t<awaitable_result_t<Awaitable>>;
619   619  
620   Range* range_; 620   Range* range_;
621 - when_any_io_homogeneous_state<PayloadT>* state_; 621 + when_any_results_homogeneous_state<PayloadT>* state_;
622   622  
623   public: 623   public:
HITCBC 624 - 20 when_any_io_homogeneous_launcher( 624 + 20 when_any_results_homogeneous_launcher(
625   Range* range, 625   Range* range,
626 - when_any_io_homogeneous_state<PayloadT>* state) 626 + when_any_results_homogeneous_state<PayloadT>* state)
HITCBC 627   20 : range_(range) 627   20 : range_(range)
HITCBC 628   20 , state_(state) 628   20 , state_(state)
629   { 629   {
HITCBC 630   20 } 630   20 }
631   631  
HITCBC 632   20 bool await_ready() const noexcept 632   20 bool await_ready() const noexcept
633   { 633   {
HITCBC 634   20 return std::ranges::empty(*range_); 634   20 return std::ranges::empty(*range_);
635   } 635   }
636   636  
HITCBC 637   20 std::coroutine_handle<> await_suspend( 637   20 std::coroutine_handle<> await_suspend(
638 - std::coroutine_handle<> continuation, io_env const* caller_env) 638 + std::coroutine_handle<> continuation, await_context const* caller_env)
639   { 639   {
HITCBC 640   20 state_->core_.continuation_.h = continuation; 640   20 state_->core_.continuation_.h = continuation;
HITCBC 641   20 state_->core_.continuation_.owner = caller_env->owner; 641   20 state_->core_.continuation_.owner = caller_env->owner;
HITCBC 642   20 state_->core_.caller_env_ = caller_env; 642   20 state_->core_.caller_env_ = caller_env;
643   643  
HITCBC 644   20 if(caller_env->stop_token.stop_possible()) 644   20 if(caller_env->stop_token.stop_possible())
645   { 645   {
HITCBC 646   4 state_->core_.parent_stop_callback_.emplace( 646   4 state_->core_.parent_stop_callback_.emplace(
HITCBC 647   2 caller_env->stop_token, 647   2 caller_env->stop_token,
HITCBC 648   2 when_any_core::stop_callback_fn{&state_->core_.stop_source_}); 648   2 when_any_core::stop_callback_fn{&state_->core_.stop_source_});
649   649  
HITCBC 650   2 if(caller_env->stop_token.stop_requested()) 650   2 if(caller_env->stop_token.stop_requested())
HITCBC 651   1 state_->core_.stop_source_.request_stop(); 651   1 state_->core_.stop_source_.request_stop();
652   } 652   }
653   653  
HITCBC 654   20 auto token = state_->core_.stop_source_.get_token(); 654   20 auto token = state_->core_.stop_source_.get_token();
655   655  
656   // Phase 1: Create all runners without dispatching. 656   // Phase 1: Create all runners without dispatching.
HITCBC 657   20 std::size_t index = 0; 657   20 std::size_t index = 0;
HITCBC 658   85 for(auto&& a : *range_) 658   85 for(auto&& a : *range_)
659   { 659   {
HITCBC 660 - 65 auto runner = make_when_any_io_homogeneous_runner( 660 + 65 auto runner = make_when_any_results_homogeneous_runner(
661   &a, state_, index); 661   &a, state_, index);
662   662  
HITCBC 663   65 auto h = runner.release(); 663   65 auto h = runner.release();
HITCBC 664   65 h.promise().state_ = state_; 664   65 h.promise().state_ = state_;
HITCBC 665   65 h.promise().index_ = index; 665   65 h.promise().index_ = index;
HITCBC 666   65 h.promise().segment_.bind(h, caller_env->executor); 666   65 h.promise().segment_.bind(h, caller_env->executor);
HITCBC 667 - 65 h.promise().env_ = io_env{caller_env->executor, token, 667 + 65 h.promise().env_ = await_context{caller_env->executor, token,
HITCBC 668   130 caller_env->frame_allocator, &h.promise().segment_}; 668   130 caller_env->frame_allocator, &h.promise().segment_};
669   669  
HITCBC 670   65 state_->runner_handles_[index].h = std::coroutine_handle<>{h}; 670   65 state_->runner_handles_[index].h = std::coroutine_handle<>{h};
HITCBC 671   65 ++index; 671   65 ++index;
672   } 672   }
673   673  
674   // Phase 2: Post all runners. Any may complete synchronously. 674   // Phase 2: Post all runners. Any may complete synchronously.
HITCBC 675   20 auto* handles = state_->runner_handles_.get(); 675   20 auto* handles = state_->runner_handles_.get();
HITCBC 676   20 std::size_t count = state_->core_.remaining_count_.load(std::memory_order_relaxed); 676   20 std::size_t count = state_->core_.remaining_count_.load(std::memory_order_relaxed);
HITCBC 677   85 for(std::size_t i = 0; i < count; ++i) 677   85 for(std::size_t i = 0; i < count; ++i)
HITCBC 678   65 caller_env->executor.post(handles[i]); 678   65 caller_env->executor.post(handles[i]);
679   679  
HITCBC 680   40 return std::noop_coroutine(); 680   40 return std::noop_coroutine();
HITCBC 681   85 } 681   85 }
682   682  
HITCBC 683   18 void await_resume() const noexcept {} 683   18 void await_resume() const noexcept {}
684   }; 684   };
685   685  
686   } // namespace detail 686   } // namespace detail
687   687  
688 - /** Race a range of io_result-returning awaitables (non-void payloads). 688 + /** Race a range of `results`-returning awaitables (non-void payloads).
689   689  
690   Only a child returning !ec can win. Errors and exceptions do not 690   Only a child returning !ec can win. Errors and exceptions do not
691   claim winner status. If all children fail, an unspecified one of 691   claim winner status. If all children fail, an unspecified one of
692   the failures is reported — either an error_code at variant index 0, 692   the failures is reported — either an error_code at variant index 0,
693   or a child's exception rethrown. 693   or a child's exception rethrown.
694   694  
695   @par Await-effects 695   @par Await-effects
696   696  
697   Takes ownership of the range, creates one wrapper coroutine per 697   Takes ownership of the range, creates one wrapper coroutine per
698   element, then posts every wrapper to the caller's executor. All 698   element, then posts every wrapper to the caller's executor. All
699   children therefore run concurrently, each awaited with the caller's 699   children therefore run concurrently, each awaited with the caller's
700   executor and frame allocator and with a stop token owned by this 700   executor and frame allocator and with a stop token owned by this
701   operation. 701   operation.
702   702  
703   Awaiting an empty range throws `std::invalid_argument` before any 703   Awaiting an empty range throws `std::invalid_argument` before any
704   child is started. 704   child is started.
705   705  
706   The first child to await-return a zero `ec` claims the win. Claiming 706   The first child to await-return a zero `ec` claims the win. Claiming
707   the win requests stop on the operation's own stop token, which every 707   the win requests stop on the operation's own stop token, which every
708   sibling observes through the stop token it was awaited with. A child 708   sibling observes through the stop token it was awaited with. A child
709   that await-returns a non-zero `ec`, or that exits via an exception, 709   that await-returns a non-zero `ec`, or that exits via an exception,
710   does not claim the win and does not request stop. The operation keeps 710   does not claim the win and does not request stop. The operation keeps
711   waiting for a success. A stop request on the caller's stop token is 711   waiting for a success. A stop request on the caller's stop token is
712   also forwarded to every child. 712   also forwarded to every child.
713   713  
714   The await completes only after every child has finished, regardless of 714   The await completes only after every child has finished, regardless of
715   whether a win was claimed. 715   whether a win was claimed.
716   716  
717   @par Await-returns 717   @par Await-returns
718   An object of type 718   An object of type
719   `std::variant<std::error_code, std::pair<std::size_t, PayloadT>>`, 719   `std::variant<std::error_code, std::pair<std::size_t, PayloadT>>`,
720 - where `PayloadT` is the payload of one child's `io_result`. 720 + where `PayloadT` is the payload of one child's `results`.
721   721  
722   @li Index 1 holds the winner's position in the input range paired 722   @li Index 1 holds the winner's position in the input range paired
723   with its payload. 723   with its payload.
724   @li Index 0 holds a non-zero `error_code` when no child won, that is, 724   @li Index 0 holds a non-zero `error_code` when no child won, that is,
725   when every child failed. It is the `ec` of one of the failed 725   when every child failed. It is the `ec` of one of the failed
726   children; which one is unspecified. 726   children; which one is unspecified.
727   727  
728   A child that succeeds after the win has already been claimed 728   A child that succeeds after the win has already been claimed
729   contributes nothing: its payload is discarded. 729   contributes nothing: its payload is discarded.
730   730  
731   If no child won and the failure selected for reporting is an 731   If no child won and the failure selected for reporting is an
732   exception rather than an `ec`, that exception is rethrown instead of 732   exception rather than an `ec`, that exception is rethrown instead of
733   await-returning. The choice of child is unspecified. 733   await-returning. The choice of child is unspecified.
734   734  
735   @par Await-postcondition 735   @par Await-postcondition
736   Every child has finished. If at least one child await-returned a zero 736   Every child has finished. If at least one child await-returned a zero
737   `ec`, the result holds index 1. If producing the winner's payload 737   `ec`, the result holds index 1. If producing the winner's payload
738   threw, that exception is rethrown. Otherwise the result holds index 0, 738   threw, that exception is rethrown. Otherwise the result holds index 0,
739   or a failed child's exception is rethrown. 739   or a failed child's exception is rethrown.
740   740  
741   @par Remarks 741   @par Remarks
742 - Supports _IoAwaitable cancellation_. A canceled child await-returns a 742 + Supports _ContextAwaitable cancellation_. A canceled child await-returns a
743   non-zero `ec` and so cannot win; if no child has already succeeded, 743   non-zero `ec` and so cannot win; if no child has already succeeded,
744   the result settles at index 0. 744   the result settles at index 0.
745   745  
746   @par Thread Safety 746   @par Thread Safety
747   The returned task must be awaited from a single execution context. 747   The returned task must be awaited from a single execution context.
748   Child awaitables execute concurrently but complete through the caller's 748   Child awaitables execute concurrently but complete through the caller's
749   executor. 749   executor.
750   750  
751 - @param awaitables Range of io_result-returning awaitables (must 751 + @param awaitables Range of `results`-returning awaitables (must
752   not be empty). 752   not be empty).
753   753  
754   @return A task yielding variant<error_code, pair<size_t, PayloadT>> 754   @return A task yielding variant<error_code, pair<size_t, PayloadT>>
755   where index 0 is failure and index 1 carries the winner's 755   where index 0 is failure and index 1 carries the winner's
756   index and payload. 756   index and payload.
757   757  
758   @throws std::invalid_argument if range is empty. 758   @throws std::invalid_argument if range is empty.
759   759  
760   @par Exception Safety 760   @par Exception Safety
761   The winner's exception is rethrown if extracting or 761   The winner's exception is rethrown if extracting or
762   move-constructing the winning payload throws. In that case a winner 762   move-constructing the winning payload throws. In that case a winner
763   was found, but its result could not be produced. If all children 763   was found, but its result could not be produced. If all children
764   fail and the reported failure is an exception, that child's 764   fail and the reported failure is an exception, that child's
765   exception is rethrown (which child is unspecified). 765   exception is rethrown (which child is unspecified).
766   766  
767   @note If a branch's coroutine is abandoned before it finishes, for 767   @note If a branch's coroutine is abandoned before it finishes, for
768   example by teardown of its execution context, `when_any` 768   example by teardown of its execution context, `when_any`
769   requests stop on the remaining branches, and the awaiting 769   requests stop on the remaining branches, and the awaiting
770   parent is abandoned rather than resumed once the last branch 770   parent is abandoned rather than resumed once the last branch
771   has finished or been abandoned. 771   has finished or been abandoned.
772   772  
773   @par Example 773   @par Example
774   @par !example example_1 774   @par !example example_1
775   775  
776   776  
777 - @see IoAwaitableRange, when_any 777 + @see ContextAwaitableRange, when_any
778   */ 778   */
779 - template<IoAwaitableRange R> 779 + template<ContextAwaitableRange R>
780 - requires detail::is_io_result_v< 780 + requires detail::is_results_v<
781   awaitable_result_t<std::ranges::range_value_t<R>>> 781   awaitable_result_t<std::ranges::range_value_t<R>>>
782   && (!std::is_same_v< 782   && (!std::is_same_v<
783 - detail::io_result_payload_t< 783 + detail::results_payload_t<
784   awaitable_result_t<std::ranges::range_value_t<R>>>, 784   awaitable_result_t<std::ranges::range_value_t<R>>>,
785   std::tuple<>>) 785   std::tuple<>>)
HITCBC 786   16 [[nodiscard]] auto when_any(R&& awaitables) 786   16 [[nodiscard]] auto when_any(R&& awaitables)
787   -> task<std::variant<std::error_code, 787   -> task<std::variant<std::error_code,
788   std::pair<std::size_t, 788   std::pair<std::size_t,
789 - detail::io_result_payload_t< 789 + detail::results_payload_t<
790   awaitable_result_t<std::ranges::range_value_t<R>>>>>> 790   awaitable_result_t<std::ranges::range_value_t<R>>>>>>
791   { 791   {
792   using Awaitable = std::ranges::range_value_t<R>; 792   using Awaitable = std::ranges::range_value_t<R>;
793 - using PayloadT = detail::io_result_payload_t< 793 + using PayloadT = detail::results_payload_t<
794   awaitable_result_t<Awaitable>>; 794   awaitable_result_t<Awaitable>>;
795   using result_type = std::variant<std::error_code, 795   using result_type = std::variant<std::error_code,
796   std::pair<std::size_t, PayloadT>>; 796   std::pair<std::size_t, PayloadT>>;
797   using OwnedRange = detail::owned_range_t<R>; 797   using OwnedRange = detail::owned_range_t<R>;
798   798  
799   auto count = std::ranges::size(awaitables); 799   auto count = std::ranges::size(awaitables);
800   if(count == 0) 800   if(count == 0)
801   throw std::invalid_argument("when_any requires at least one awaitable"); 801   throw std::invalid_argument("when_any requires at least one awaitable");
802   802  
803   OwnedRange owned_awaitables = detail::own_range(std::forward<R>(awaitables)); 803   OwnedRange owned_awaitables = detail::own_range(std::forward<R>(awaitables));
804   804  
805 - detail::when_any_io_homogeneous_state<PayloadT> state(count); 805 + detail::when_any_results_homogeneous_state<PayloadT> state(count);
806   806  
807 - co_await detail::when_any_io_homogeneous_launcher<OwnedRange>( 807 + co_await detail::when_any_results_homogeneous_launcher<OwnedRange>(
808   &owned_awaitables, &state); 808   &owned_awaitables, &state);
809   809  
810   // Winner found 810   // Winner found
811   if(state.core_.has_winner_.load(std::memory_order_acquire)) 811   if(state.core_.has_winner_.load(std::memory_order_acquire))
812   { 812   {
813   if(state.core_.winner_exception_) 813   if(state.core_.winner_exception_)
814   std::rethrow_exception(state.core_.winner_exception_); 814   std::rethrow_exception(state.core_.winner_exception_);
815   co_return result_type{std::in_place_index<1>, 815   co_return result_type{std::in_place_index<1>,
816   std::pair{state.core_.winner_index_, std::move(*state.result_)}}; 816   std::pair{state.core_.winner_index_, std::move(*state.result_)}};
817   } 817   }
818   818  
819   // No winner — report the recorded failure 819   // No winner — report the recorded failure
820   if(state.last_exception_) 820   if(state.last_exception_)
821   std::rethrow_exception(state.last_exception_); 821   std::rethrow_exception(state.last_exception_);
822   co_return result_type{std::in_place_index<0>, state.last_error_}; 822   co_return result_type{std::in_place_index<0>, state.last_error_};
HITCBC 823   32 } 823   32 }
824   824  
825 - /** Race a range of void io_result-returning awaitables. 825 + /** Race a range of void `results`-returning awaitables.
826   826  
827   Only a child returning !ec can win. Returns the winner's index 827   Only a child returning !ec can win. Returns the winner's index
828   at variant index 1, or error_code at index 0 on all-fail. 828   at variant index 1, or error_code at index 0 on all-fail.
829   829  
830   @par Await-effects 830   @par Await-effects
831   831  
832   Takes ownership of the range, creates one wrapper coroutine per 832   Takes ownership of the range, creates one wrapper coroutine per
833   element, then posts every wrapper to the caller's executor. All 833   element, then posts every wrapper to the caller's executor. All
834   children therefore run concurrently, each awaited with the caller's 834   children therefore run concurrently, each awaited with the caller's
835   executor and frame allocator and with a stop token owned by this 835   executor and frame allocator and with a stop token owned by this
836   operation. 836   operation.
837   837  
838   Awaiting an empty range throws `std::invalid_argument` before any 838   Awaiting an empty range throws `std::invalid_argument` before any
839   child is started. 839   child is started.
840   840  
841   The first child to await-return a zero `ec` claims the win. Claiming 841   The first child to await-return a zero `ec` claims the win. Claiming
842   the win requests stop on the operation's own stop token, which every 842   the win requests stop on the operation's own stop token, which every
843   sibling observes through the stop token it was awaited with. A child 843   sibling observes through the stop token it was awaited with. A child
844   that await-returns a non-zero `ec`, or that exits via an exception, 844   that await-returns a non-zero `ec`, or that exits via an exception,
845   does not claim the win and does not request stop. The operation keeps 845   does not claim the win and does not request stop. The operation keeps
846   waiting for a success. A stop request on the caller's stop token is 846   waiting for a success. A stop request on the caller's stop token is
847   also forwarded to every child. 847   also forwarded to every child.
848   848  
849   The await completes only after every child has finished, regardless of 849   The await completes only after every child has finished, regardless of
850   whether a win was claimed. 850   whether a win was claimed.
851   851  
852   @par Await-returns 852   @par Await-returns
853   An object of type `std::variant<std::error_code, std::size_t>`. 853   An object of type `std::variant<std::error_code, std::size_t>`.
854   854  
855   @li Index 1 holds the winner's position in the input range. The 855   @li Index 1 holds the winner's position in the input range. The
856   children have no payloads, so nothing else is reported. 856   children have no payloads, so nothing else is reported.
857   @li Index 0 holds a non-zero `error_code` when no child won, that is, 857   @li Index 0 holds a non-zero `error_code` when no child won, that is,
858   when every child failed. It is the `ec` of one of the failed 858   when every child failed. It is the `ec` of one of the failed
859   children; which one is unspecified. 859   children; which one is unspecified.
860   860  
861   If no child won and the failure selected for reporting is an 861   If no child won and the failure selected for reporting is an
862   exception rather than an `ec`, that exception is rethrown instead of 862   exception rather than an `ec`, that exception is rethrown instead of
863   await-returning. The choice of child is unspecified. 863   await-returning. The choice of child is unspecified.
864   864  
865   @par Await-postcondition 865   @par Await-postcondition
866   Every child has finished. The result holds index 1 if at least one 866   Every child has finished. The result holds index 1 if at least one
867   child await-returned a zero `ec`. Otherwise the result holds index 0, 867   child await-returned a zero `ec`. Otherwise the result holds index 0,
868   or a failed child's exception is rethrown. 868   or a failed child's exception is rethrown.
869   869  
870   @par Remarks 870   @par Remarks
871 - Supports _IoAwaitable cancellation_. A canceled child await-returns a 871 + Supports _ContextAwaitable cancellation_. A canceled child await-returns a
872   non-zero `ec` and so cannot win; if no child has already succeeded, 872   non-zero `ec` and so cannot win; if no child has already succeeded,
873   the result settles at index 0. 873   the result settles at index 0.
874   874  
875   @par Thread Safety 875   @par Thread Safety
876   The returned task must be awaited from a single execution context. 876   The returned task must be awaited from a single execution context.
877   Child awaitables execute concurrently but complete through the caller's 877   Child awaitables execute concurrently but complete through the caller's
878   executor. 878   executor.
879   879  
880 - @param awaitables Range of io_result<>-returning awaitables (must 880 + @param awaitables Range of `results<>`-returning awaitables (must
881   not be empty). 881   not be empty).
882   882  
883   @return A task yielding variant<error_code, size_t> where index 0 883   @return A task yielding variant<error_code, size_t> where index 0
884   is failure and index 1 carries the winner's index. 884   is failure and index 1 carries the winner's index.
885   885  
886   @throws std::invalid_argument if range is empty. 886   @throws std::invalid_argument if range is empty.
887   887  
888   @par Exception Safety 888   @par Exception Safety
889   If all children fail and the reported failure is an exception, 889   If all children fail and the reported failure is an exception,
890   that child's exception is rethrown (which child is unspecified). 890   that child's exception is rethrown (which child is unspecified).
891   891  
892   @note If a branch's coroutine is abandoned before it finishes, for 892   @note If a branch's coroutine is abandoned before it finishes, for
893   example by teardown of its execution context, `when_any` 893   example by teardown of its execution context, `when_any`
894   requests stop on the remaining branches, and the awaiting 894   requests stop on the remaining branches, and the awaiting
895   parent is abandoned rather than resumed once the last branch 895   parent is abandoned rather than resumed once the last branch
896   has finished or been abandoned. 896   has finished or been abandoned.
897   897  
898   @par Example 898   @par Example
899   @par !example example_2 899   @par !example example_2
900   900  
901   901  
902 - @see IoAwaitableRange, when_any 902 + @see ContextAwaitableRange, when_any
903   */ 903   */
904 - template<IoAwaitableRange R> 904 + template<ContextAwaitableRange R>
905 - requires detail::is_io_result_v< 905 + requires detail::is_results_v<
906   awaitable_result_t<std::ranges::range_value_t<R>>> 906   awaitable_result_t<std::ranges::range_value_t<R>>>
907   && std::is_same_v< 907   && std::is_same_v<
908 - detail::io_result_payload_t< 908 + detail::results_payload_t<
909   awaitable_result_t<std::ranges::range_value_t<R>>>, 909   awaitable_result_t<std::ranges::range_value_t<R>>>,
910   std::tuple<>> 910   std::tuple<>>
HITCBC 911   5 [[nodiscard]] auto when_any(R&& awaitables) 911   5 [[nodiscard]] auto when_any(R&& awaitables)
912   -> task<std::variant<std::error_code, std::size_t>> 912   -> task<std::variant<std::error_code, std::size_t>>
913   { 913   {
914   using OwnedRange = detail::owned_range_t<R>; 914   using OwnedRange = detail::owned_range_t<R>;
915   using result_type = std::variant<std::error_code, std::size_t>; 915   using result_type = std::variant<std::error_code, std::size_t>;
916   916  
917   auto count = std::ranges::size(awaitables); 917   auto count = std::ranges::size(awaitables);
918   if(count == 0) 918   if(count == 0)
919   throw std::invalid_argument("when_any requires at least one awaitable"); 919   throw std::invalid_argument("when_any requires at least one awaitable");
920   920  
921   OwnedRange owned_awaitables = detail::own_range(std::forward<R>(awaitables)); 921   OwnedRange owned_awaitables = detail::own_range(std::forward<R>(awaitables));
922   922  
923 - detail::when_any_io_homogeneous_state<std::tuple<>> state(count); 923 + detail::when_any_results_homogeneous_state<std::tuple<>> state(count);
924   924  
925 - co_await detail::when_any_io_homogeneous_launcher<OwnedRange>( 925 + co_await detail::when_any_results_homogeneous_launcher<OwnedRange>(
926   &owned_awaitables, &state); 926   &owned_awaitables, &state);
927   927  
928   // Winner found 928   // Winner found
929   if(state.core_.has_winner_.load(std::memory_order_acquire)) 929   if(state.core_.has_winner_.load(std::memory_order_acquire))
930   { 930   {
931   if(state.core_.winner_exception_) 931   if(state.core_.winner_exception_)
932   std::rethrow_exception(state.core_.winner_exception_); 932   std::rethrow_exception(state.core_.winner_exception_);
933   co_return result_type{std::in_place_index<1>, 933   co_return result_type{std::in_place_index<1>,
934   state.core_.winner_index_}; 934   state.core_.winner_index_};
935   } 935   }
936   936  
937   // No winner — report the recorded failure 937   // No winner — report the recorded failure
938   if(state.last_exception_) 938   if(state.last_exception_)
939   std::rethrow_exception(state.last_exception_); 939   std::rethrow_exception(state.last_exception_);
940   co_return result_type{std::in_place_index<0>, state.last_error_}; 940   co_return result_type{std::in_place_index<0>, state.last_error_};
HITCBC 941   10 } 941   10 }
942   942  
943 - /** Race io_result-returning awaitables, selecting the first success. 943 + /** Race `results`-returning awaitables, selecting the first success.
944   944  
945 - Overload selected when all children return io_result<Ts...>. 945 + Overload selected when all children return results<Ts...>.
946   Only a child returning !ec can win. Errors and exceptions do 946   Only a child returning !ec can win. Errors and exceptions do
947   not claim winner status. 947   not claim winner status.
948   948  
949   @par Await-effects 949   @par Await-effects
950   950  
951   Creates and posts one wrapper coroutine per argument to the caller's 951   Creates and posts one wrapper coroutine per argument to the caller's
952   executor, in argument order. All children therefore run concurrently, 952   executor, in argument order. All children therefore run concurrently,
953   each awaited with the caller's executor and frame allocator and with 953   each awaited with the caller's executor and frame allocator and with
954   a stop token owned by this operation. The overload requires at least 954   a stop token owned by this operation. The overload requires at least
955   one awaitable, so there is no empty case. 955   one awaitable, so there is no empty case.
956   956  
957   The first child to await-return a zero `ec` claims the win. Claiming 957   The first child to await-return a zero `ec` claims the win. Claiming
958   the win requests stop on the operation's own stop token, which every 958   the win requests stop on the operation's own stop token, which every
959   sibling observes through the stop token it was awaited with. A child 959   sibling observes through the stop token it was awaited with. A child
960   that await-returns a non-zero `ec`, or that exits via an exception, 960   that await-returns a non-zero `ec`, or that exits via an exception,
961   does not claim the win and does not request stop. The operation keeps 961   does not claim the win and does not request stop. The operation keeps
962   waiting for a success. A stop request on the caller's stop token is 962   waiting for a success. A stop request on the caller's stop token is
963   also forwarded to every child. 963   also forwarded to every child.
964   964  
965   The await completes only after every child has finished, regardless of 965   The await completes only after every child has finished, regardless of
966   whether a win was claimed. 966   whether a win was claimed.
967   967  
968   @par Await-returns 968   @par Await-returns
969   An object of type `std::variant<std::error_code, P1, ..., Pn>`, where 969   An object of type `std::variant<std::error_code, P1, ..., Pn>`, where
970 - `Pi` is the payload of the i-th child's `io_result`. 970 + `Pi` is the payload of the i-th child's `results`.
971   971  
972   @li Index i+1 identifies the i-th argument as the winner and holds 972   @li Index i+1 identifies the i-th argument as the winner and holds
973   its payload. 973   its payload.
974   @li Index 0 holds a non-zero `error_code` when no child won, that is, 974   @li Index 0 holds a non-zero `error_code` when no child won, that is,
975   when every child failed. It is the `ec` of one of the failed 975   when every child failed. It is the `ec` of one of the failed
976   children; which one is unspecified. 976   children; which one is unspecified.
977   977  
978   A child that succeeds after the win has already been claimed 978   A child that succeeds after the win has already been claimed
979   contributes nothing: its payload is discarded. 979   contributes nothing: its payload is discarded.
980   980  
981   If no child won and the failure selected for reporting is an 981   If no child won and the failure selected for reporting is an
982   exception rather than an `ec`, that exception is rethrown instead of 982   exception rather than an `ec`, that exception is rethrown instead of
983   await-returning. The choice of child is unspecified. 983   await-returning. The choice of child is unspecified.
984   984  
985   @par Await-postcondition 985   @par Await-postcondition
986   Every child has finished. If at least one child await-returned a zero 986   Every child has finished. If at least one child await-returned a zero
987   `ec`, the result holds the index of the winning child. If producing 987   `ec`, the result holds the index of the winning child. If producing
988   the winner's payload threw, that exception is rethrown. Otherwise the 988   the winner's payload threw, that exception is rethrown. Otherwise the
989   result holds index 0, or a failed child's exception is rethrown. 989   result holds index 0, or a failed child's exception is rethrown.
990   990  
991   @par Remarks 991   @par Remarks
992 - Supports _IoAwaitable cancellation_. A canceled child await-returns a 992 + Supports _ContextAwaitable cancellation_. A canceled child await-returns a
993   non-zero `ec` and so cannot win; if no child has already succeeded, 993   non-zero `ec` and so cannot win; if no child has already succeeded,
994   the result settles at index 0. 994   the result settles at index 0.
995   995  
996   @par Thread Safety 996   @par Thread Safety
997   The returned task must be awaited from a single execution context. 997   The returned task must be awaited from a single execution context.
998   Child awaitables execute concurrently but complete through the caller's 998   Child awaitables execute concurrently but complete through the caller's
999   executor. 999   executor.
1000   1000  
1001   @param as The awaitables to race. Each must satisfy @ref 1001   @param as The awaitables to race. Each must satisfy @ref
1002 - IoAwaitable and is consumed (moved-from) when `when_any` 1002 + ContextAwaitable and is consumed (moved-from) when `when_any`
1003   is awaited. 1003   is awaited.
1004   1004  
1005   @return A task yielding variant<error_code, R1, ..., Rn> where 1005   @return A task yielding variant<error_code, R1, ..., Rn> where
1006   index 0 is the failure/no-winner case and index i+1 1006   index 0 is the failure/no-winner case and index i+1
1007   identifies the winning child. On all-fail, index 0 holds 1007   identifies the winning child. On all-fail, index 0 holds
1008   an error_code from one of the failed children (unspecified 1008   an error_code from one of the failed children (unspecified
1009   which; no priority between errors and exceptions). 1009   which; no priority between errors and exceptions).
1010   1010  
1011   @par Exception Safety 1011   @par Exception Safety
1012   The winner's exception is rethrown if extracting or constructing 1012   The winner's exception is rethrown if extracting or constructing
1013   the winning payload throws. In that case a winner was found, but 1013   the winning payload throws. In that case a winner was found, but
1014   its result could not be produced. If all children fail and the 1014   its result could not be produced. If all children fail and the
1015   reported failure is an exception, that child's exception is 1015   reported failure is an exception, that child's exception is
1016   rethrown (which child is unspecified). 1016   rethrown (which child is unspecified).
1017   1017  
1018   @note A failing child does not cancel its siblings; `when_any` 1018   @note A failing child does not cancel its siblings; `when_any`
1019   waits for a success or for every child to finish. To make a 1019   waits for a success or for every child to finish. To make a
1020   benign error (e.g. @c cond::canceled) count as a win, wrap 1020   benign error (e.g. @c cond::canceled) count as a win, wrap
1021   the child to translate the error into success. See the 1021   the child to translate the error into success. See the
1022   Concurrent Composition tutorial. 1022   Concurrent Composition tutorial.
1023   1023  
1024   @note If a branch's coroutine is abandoned before it finishes, for 1024   @note If a branch's coroutine is abandoned before it finishes, for
1025   example by teardown of its execution context, `when_any` 1025   example by teardown of its execution context, `when_any`
1026   requests stop on the remaining branches, and the awaiting 1026   requests stop on the remaining branches, and the awaiting
1027   parent is abandoned rather than resumed once the last branch 1027   parent is abandoned rather than resumed once the last branch
1028   has finished or been abandoned. 1028   has finished or been abandoned.
1029   */ 1029   */
1030 - template<IoAwaitable... As> 1030 + template<ContextAwaitable... As>
1031   requires (sizeof...(As) > 0) 1031   requires (sizeof...(As) > 0)
1032 - && detail::all_io_result_awaitables<As...> 1032 + && detail::all_results_awaitables<As...>
HITCBC 1033   33 [[nodiscard]] auto when_any(As... as) 1033   33 [[nodiscard]] auto when_any(As... as)
1034   -> task<std::variant< 1034   -> task<std::variant<
1035   std::error_code, 1035   std::error_code,
1036 - detail::io_result_payload_t<awaitable_result_t<As>>...>> 1036 + detail::results_payload_t<awaitable_result_t<As>>...>>
1037   { 1037   {
1038   using result_type = std::variant< 1038   using result_type = std::variant<
1039   std::error_code, 1039   std::error_code,
1040 - detail::io_result_payload_t<awaitable_result_t<As>>...>; 1040 + detail::results_payload_t<awaitable_result_t<As>>...>;
1041   1041  
1042 - detail::when_any_io_state< 1042 + detail::when_any_results_state<
1043 - detail::io_result_payload_t<awaitable_result_t<As>>...> state; 1043 + detail::results_payload_t<awaitable_result_t<As>>...> state;
1044   std::tuple<As...> awaitable_tuple(std::move(as)...); 1044   std::tuple<As...> awaitable_tuple(std::move(as)...);
1045   1045  
1046 - co_await detail::when_any_io_launcher<As...>( 1046 + co_await detail::when_any_results_launcher<As...>(
1047   &awaitable_tuple, &state); 1047   &awaitable_tuple, &state);
1048   1048  
1049   // Winner found: return their result 1049   // Winner found: return their result
1050   if(state.result_.has_value()) 1050   if(state.result_.has_value())
1051   co_return std::move(*state.result_); 1051   co_return std::move(*state.result_);
1052   1052  
1053   // Winner claimed but payload construction failed 1053   // Winner claimed but payload construction failed
1054   if(state.core_.winner_exception_) 1054   if(state.core_.winner_exception_)
1055   std::rethrow_exception(state.core_.winner_exception_); 1055   std::rethrow_exception(state.core_.winner_exception_);
1056   1056  
1057   // No winner — report the recorded failure 1057   // No winner — report the recorded failure
1058   if(state.last_exception_) 1058   if(state.last_exception_)
1059   std::rethrow_exception(state.last_exception_); 1059   std::rethrow_exception(state.last_exception_);
1060   co_return result_type{std::in_place_index<0>, state.last_error_}; 1060   co_return result_type{std::in_place_index<0>, state.last_error_};
HITCBC 1061   66 } 1061   66 }
1062   1062  
1063   } // namespace capy 1063   } // namespace capy
1064   } // namespace boost 1064   } // namespace boost
1065   1065  
1066   #endif 1066   #endif