Aria
2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Toggle main menu visibility
Loading...
Searching...
No Matches
async_command_builder.hpp
Go to the documentation of this file.
1
#pragma once
2
3
// AsyncCommand action wrappers — `action_with_timeout` and
4
// `action_with_retry`.
5
//
6
// These compose the existing `aria::async::with_timeout` and
7
// `aria::async::retry_with_backoff` / `retry_if` combinators into an
8
// AsyncCommand-friendly form, so a user can write:
9
//
10
// AsyncCommand<Profile, std::string> load_profile(
11
// ui, worker,
12
// async::action_with_retry<Profile, std::string>(
13
// 3, 200ms, timer,
14
// async::action_with_timeout<Profile, std::string>(
15
// timer, 3s,
16
// [this](CancellationToken tok, std::string id) -> Task<Profile> {
17
// return api_->fetch_profile(tok, std::move(id));
18
// })));
19
//
20
// Composition semantics:
21
// * Each retry attempt gets its OWN timeout (the inner factory is
22
// re-invoked per attempt — that is the contract of with_timeout +
23
// retry already documented in timeout.hpp).
24
// * External cancellation (the AsyncCommand's per-invocation
25
// CancellationToken) propagates into the timeout's parent token,
26
// so cancelling the AsyncCommand stops both the in-flight attempt
27
// AND any pending retries instantly.
28
// * `action_with_retry`'s `should_retry` predicate, when supplied,
29
// gates retries on `std::exception&` (including `TimeoutError`).
30
// Pass nullptr (default) to retry on every exception.
31
32
#include "
aria/async/async_command.hpp
"
33
#include "
aria/async/cancellation.hpp
"
34
#include "
aria/async/retry.hpp
"
35
#include "
aria/async/task.hpp
"
36
#include "
aria/async/timeout.hpp
"
37
#include "
aria/async/virtual_time_executor.hpp
"
// IDelayedScheduler
38
39
#include <chrono>
40
#include <exception>
41
#include <functional>
42
#include <type_traits>
43
#include <utility>
44
45
namespace
aria::async
{
46
55
template
<
typename
R,
typename
... Args,
typename
Fn>
56
auto
action_with_timeout
(
IDelayedScheduler
& timer,
57
std::chrono::milliseconds duration,
58
Fn factory)
59
-> std::function<Task<R>(
CancellationToken
, Args...)>
60
{
61
return
[&timer, duration, factory = std::move(factory)](
62
CancellationToken
parent_tok, Args... args)
mutable
->
Task<R>
{
63
co_return
co_await
aria::async::with_timeout
(
64
parent_tok, timer, duration,
65
[factory, args...](
CancellationToken
inner_tok)
mutable
{
66
return factory(inner_tok, args...);
67
});
68
};
69
}
70
83
template
<
typename
R,
typename
... Args,
typename
Fn>
84
auto
action_with_retry
(
int
max_attempts,
85
std::chrono::milliseconds initial_backoff,
86
IDelayedScheduler
& timer,
87
Fn factory,
88
std::function<
bool
(
const
std::exception&)>
89
should_retry =
nullptr
)
90
-> std::function<Task<R>(
CancellationToken
, Args...)>
91
{
92
return
[max_attempts, initial_backoff, &timer,
93
factory = std::move(factory),
94
should_retry = std::move(should_retry)](
95
CancellationToken
parent_tok, Args... args)
mutable
->
Task<R>
{
96
// Bind the user args into a nullary factory the retry combinator
97
// can re-invoke per attempt. Each attempt shares `parent_tok`
98
// so external cancel flips every in-flight attempt.
99
auto
bound = [factory, args..., parent_tok]()
mutable
{
100
return
factory(parent_tok, args...);
101
};
102
103
// Delay schedule: 1x, 2x, 4x, 8x ... of initial_backoff.
104
auto
next_delay = [initial_backoff](
int
attempt)
105
-> std::chrono::milliseconds {
106
return
detail::retry_delay_(initial_backoff, attempt);
107
};
108
109
// `should_retry` routes directly into retry_impl_ so the
110
// predicate is actually honoured (earlier drafts double-threw
111
// and silently lost the predicate — that was the bug this
112
// shape fixes).
113
auto
predicate = should_retry
114
? should_retry
115
: std::function<bool(const std::exception&)>(
116
[](
const
std::exception&) {
return
true
; });
117
118
if
constexpr
(std::is_void_v<R>) {
119
co_await
detail::retry_impl_(
120
max_attempts, predicate, next_delay, &timer, std::move(bound), parent_tok);
121
co_return
;
122
}
else
{
123
co_return
co_await
detail::retry_impl_(
124
max_attempts, predicate, next_delay, &timer, std::move(bound), parent_tok);
125
}
126
};
127
}
128
129
}
// namespace aria::async
async_command.hpp
cancellation.hpp
aria::IDelayedScheduler
Tiny interface — anything that can post a function to run after a delay.
Definition
property_ops.hpp:62
aria::async::CancellationToken
Definition
cancellation.hpp:182
aria::async::Task
Definition
task.hpp:78
aria::async
Definition
async_command.hpp:118
aria::async::action_with_timeout
auto action_with_timeout(IDelayedScheduler &timer, std::chrono::milliseconds duration, Fn factory) -> std::function< Task< R >(CancellationToken, Args...)>
Wrap an AsyncCommand action factory with a per-invocation timeout.
Definition
async_command_builder.hpp:56
aria::async::action_with_retry
auto action_with_retry(int max_attempts, std::chrono::milliseconds initial_backoff, IDelayedScheduler &timer, Fn factory, std::function< bool(const std::exception &)> should_retry=nullptr) -> std::function< Task< R >(CancellationToken, Args...)>
Wrap an AsyncCommand action with retry + exponential backoff.
Definition
async_command_builder.hpp:84
aria::async::with_timeout
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
retry.hpp
task.hpp
timeout.hpp
virtual_time_executor.hpp
modules
async
include
aria
async
async_command_builder.hpp
Generated by
1.18.0