69#include "aria/async/detail/race_slot.hpp"
70#include "aria/async/detail/race_trace.hpp"
116template<
typename Factory>
117concept TimeoutTokenAcceptingFactory =
118 std::invocable<Factory, CancellationToken>;
120template<
typename Factory>
121concept TimeoutPlainFactory =
122 std::invocable<Factory>;
124template<
typename Factory>
125struct timeout_factory_value;
127template<
typename Factory>
128 requires TimeoutTokenAcceptingFactory<Factory>
129struct timeout_factory_value<Factory> {
130 using type =
typename std::invoke_result_t<Factory, CancellationToken>::promise_type::value_type;
133template<
typename Factory>
134 requires (!TimeoutTokenAcceptingFactory<Factory>) && TimeoutPlainFactory<Factory>
135struct timeout_factory_value<Factory> {
136 using type =
typename std::invoke_result_t<Factory>::promise_type::value_type;
139template<
typename Factory>
140using timeout_factory_value_t =
typename timeout_factory_value<Factory>::type;
143template<
typename Factory>
144auto invoke_factory(Factory& f, CancellationToken tok) {
145 if constexpr (TimeoutTokenAcceptingFactory<Factory>) {
160template<
typename Factory>
161auto with_timeout_impl_(std::optional<CancellationToken> parent,
162 IDelayedScheduler& timer,
163 std::chrono::milliseconds duration,
165 -> Task<timeout_factory_value_t<Factory>>
167 using R = timeout_factory_value_t<Factory>;
171 auto inner_src = std::make_shared<CancellationSource>();
172 auto inner_tok = inner_src->token();
175 auto winner = std::make_shared<std::atomic<int>>(0);
177 publish_race_trace(race_source::kWithTimeout, race_op::kStart);
180 publish_race_trace(race_source::kWithTimeout, race_op::kEnd);
186 timer.post_after(duration, [winner, inner_src]() {
188 if (winner->compare_exchange_strong(expected, 2)) {
192 publish_race_trace(race_source::kWithTimeout, race_op::kTimeout, 0,
203 parent->on_cancel([inner_src]{ inner_src->cancel(); });
206 auto finish_success = [&] {
208 const bool inner_won = winner->compare_exchange_strong(expected, 1);
209 if (parent && parent->is_cancelled())
throw OperationCancelled{};
212 if (!inner_won)
throw TimeoutError{};
213 publish_race_trace(race_source::kWithTimeout, race_op::kWon);
217 if constexpr (std::is_void_v<R>) {
218 co_await invoke_factory(factory, inner_tok);
222 R value =
co_await invoke_factory(factory, inner_tok);
226 }
catch (
const OperationCancelled&) {
228 winner->compare_exchange_strong(expected, 1);
229 if (parent && parent->is_cancelled()) {
230 publish_race_trace(race_source::kWithTimeout,
231 race_op::kParentCancel, 0,
235 if (winner->load() == 2)
throw TimeoutError{};
241 winner->compare_exchange_strong(expected, 1);
260template<
typename Factory,
typename R>
261Task<void> drive_inner_for_fail_(
263 CancellationToken inner_tok,
264 std::shared_ptr<RaceSlot<R>> slot)
267 if constexpr (std::is_void_v<R>) {
268 co_await invoke_factory(factory, inner_tok);
269 if (slot->try_claim(1)) {
270 slot->store_value_or_exception();
272 publish_race_trace(race_source::kWithTimeout, race_op::kWon);
273 slot->notify_winner_resume();
277 R value =
co_await invoke_factory(factory, inner_tok);
278 if (slot->try_claim(1)) {
279 slot->store_value_or_exception(std::move(value));
281 publish_race_trace(race_source::kWithTimeout, race_op::kWon);
282 slot->notify_winner_resume();
286 if (slot->try_claim(1)) {
287 slot->result.template emplace<2>(std::current_exception());
292 publish_race_trace(race_source::kWithTimeout, race_op::kWon);
293 slot->notify_winner_resume();
302template<
typename Factory>
303auto with_timeout_fail_impl_(std::optional<CancellationToken> parent,
304 IDelayedScheduler& timer,
305 std::chrono::milliseconds duration,
307 -> Task<timeout_factory_value_t<Factory>>
309 using R = timeout_factory_value_t<Factory>;
311 auto slot = std::make_shared<RaceSlot<R>>();
312 auto inner_src = std::make_shared<CancellationSource>();
313 auto inner_tok = inner_src->token();
315 publish_race_trace(race_source::kWithTimeout, race_op::kStart);
318 publish_race_trace(race_source::kWithTimeout, race_op::kEnd);
323 timer.post_after(duration, [slot, inner_src]() {
324 if (slot->try_claim(2)) {
328 slot->result.template emplace<2>(
329 std::make_exception_ptr(TimeoutError{}));
331 publish_race_trace(race_source::kWithTimeout, race_op::kTimeout, 0,
333 slot->notify_winner_resume();
340 parent->on_cancel([slot, inner_src]{
341 if (slot->try_claim(3)) {
343 slot->result.template emplace<2>(
344 std::make_exception_ptr(OperationCancelled{}));
346 publish_race_trace(race_source::kWithTimeout,
347 race_op::kParentCancel, 0,
349 slot->notify_winner_resume();
357 drive_inner_for_fail_<Factory, R>(std::move(factory), inner_tok, slot)
362 if constexpr (std::is_void_v<R>) {
363 co_await RaceSlotAwaiter<R>{slot};
366 auto decoded =
co_await RaceSlotAwaiter<R>{slot};
375template<
typename Factory>
377 std::chrono::milliseconds duration,
383 return detail::with_timeout_fail_impl_(
384 std::optional<CancellationToken>{}, timer, duration, std::move(factory));
386 return detail::with_timeout_impl_(
387 std::optional<CancellationToken>{}, timer, duration, std::move(factory));
390template<
typename Factory>
393 std::chrono::milliseconds duration,
399 return detail::with_timeout_fail_impl_(
400 std::optional<CancellationToken>{std::move(parent)},
401 timer, duration, std::move(factory));
403 return detail::with_timeout_impl_(
404 std::optional<CancellationToken>{std::move(parent)},
405 timer, duration, std::move(factory));
Tiny interface — anything that can post a function to run after a delay.
Definition property_ops.hpp:62
Definition cancellation.hpp:182
TimeoutError()
Definition timeout.hpp:89
Definition async_command.hpp:118
OnTimeout
Behaviour when the deadline expires.
Definition timeout.hpp:109
@ Fail
Definition timeout.hpp:111
@ Cancel
Definition timeout.hpp:110
auto with_timeout(IDelayedScheduler &timer, std::chrono::milliseconds duration, Factory factory, OnTimeout on_timeout=OnTimeout::Cancel) -> Task< detail::timeout_factory_value_t< Factory > >
Definition timeout.hpp:376
Definition validation_key.hpp:110
static Error cancellation(std::string source_tag="AsyncCommand")
Cancellation.
Definition error.hpp:192
static Error timeout(std::string source_tag="AsyncCommand")
with_timeout deadline expired.
Definition error.hpp:198