45 template<
typename Factory>
46 using factory_value_t =
47 typename std::invoke_result_t<Factory>::promise_type::value_type;
52 IDelayedScheduler& scheduler;
53 std::chrono::milliseconds delay;
54 bool await_ready() const noexcept {
return false; }
55 void await_suspend(std::coroutine_handle<> h)
const {
56 scheduler.post_after(delay, [h]()
mutable { h.resume(); });
58 void await_resume() const noexcept {}
64 struct CancellableDelayAwaiter {
67 std::atomic<int> phase{0};
68 std::coroutine_handle<> handle;
70 void signal(
bool from_cancellation) {
71 if (phase.exchange(2, std::memory_order_acq_rel) != 1)
return;
72 if (from_cancellation) schedule_deferred_resume(handle);
76 IDelayedScheduler& scheduler;
77 std::chrono::milliseconds delay;
78 CancellationToken token;
79 std::shared_ptr<State> state = std::make_shared<State>();
81 bool await_ready() const noexcept {
return token.is_cancelled(); }
82 bool await_suspend(std::coroutine_handle<> h) {
85 scheduler.post_after(delay, [shared] { shared->signal(
false); });
86 token.on_cancel([shared] { shared->signal(
true); });
88 return shared->phase.compare_exchange_strong(
89 expected, 1, std::memory_order_acq_rel);
91 void await_resume()
const { token.throw_if_cancelled(); }
94 inline std::chrono::milliseconds retry_delay_(
95 std::chrono::milliseconds initial,
int attempt)
noexcept {
96 using Rep = std::chrono::milliseconds::rep;
97 if (initial.count() <= 0)
return std::chrono::milliseconds{0};
98 const auto multiplier = Rep{1} << std::clamp(attempt, 0, 20);
99 constexpr auto maximum = std::numeric_limits<Rep>::max();
100 return std::chrono::milliseconds{
101 initial.count() > maximum / multiplier
102 ? maximum : initial.count() * multiplier};
114 template<
typename Factory,
typename ShouldRetry,
typename NextDelay>
115 auto retry_impl_(
int max_attempts,
116 ShouldRetry should_retry,
117 NextDelay next_delay,
118 IDelayedScheduler* timer,
120 std::optional<CancellationToken> token = std::nullopt)
121 -> Task<factory_value_t<Factory>>
123 using R = factory_value_t<Factory>;
124 if (max_attempts <= 0) {
125 throw std::invalid_argument(
"retry: max_attempts must be positive");
127 std::exception_ptr last;
128 for (
int attempt = 0; attempt < max_attempts; ++attempt) {
130 if (token) token->throw_if_cancelled();
131 if constexpr (std::is_void_v<R>) {
135 co_return co_await factory();
137 }
catch (
const OperationCancelled&) {
146 }
catch (
const std::exception& e) {
147 last = std::current_exception();
148 const bool last_attempt = (attempt + 1 == max_attempts);
149 if (last_attempt || !should_retry(e)) {
150 std::rethrow_exception(last);
160 const auto delay = next_delay(attempt);
161 if (delay.count() > 0) {
163 co_await CancellableDelayAwaiter{*timer, delay, *token};
165 co_await DelayAwaiter{*timer, delay};
170 std::rethrow_exception(last);
175template<
typename Factory>
176auto retry(
int max_attempts, Factory factory)
179 return detail::retry_impl_(
181 [](
const std::exception&) {
return true; },
182 [](int) {
return std::chrono::milliseconds{0}; },
188template<
typename Predicate,
typename Factory>
189auto retry_if(
int max_attempts, Predicate should_retry, Factory factory)
192 return detail::retry_impl_(
194 std::move(should_retry),
195 [](
int) {
return std::chrono::milliseconds{0}; },
201template<
typename Factory>
203 std::chrono::milliseconds initial,
208 return detail::retry_impl_(
210 [](
const std::exception&) {
return true; },
211 [initial](
int attempt) {
212 return detail::retry_delay_(initial, attempt);
Tiny interface — anything that can post a function to run after a delay.
Definition property_ops.hpp:62
Definition async_command.hpp:118
auto retry_with_backoff(int max_attempts, std::chrono::milliseconds initial, IDelayedScheduler &timer, Factory factory) -> Task< detail::factory_value_t< Factory > >
Definition retry.hpp:202
auto retry_if(int max_attempts, Predicate should_retry, Factory factory) -> Task< detail::factory_value_t< Factory > >
Definition retry.hpp:189
auto retry(int max_attempts, Factory factory) -> Task< detail::factory_value_t< Factory > >
Definition retry.hpp:176