Skip to content

Commit

Permalink
🎨 Allow a sender to cause its own timeout with timeout_after
Browse files Browse the repository at this point in the history
Problem:
- `timeout_after(s, to)` cannot cause its own timeout.
- The ability to write a sender that causes its own timeout is useful for tests.

Solution:
- Start the timer sender before the sender to be timed out.
- This means the time allowed includes the time for the sender logic itself,
  rather than excluding it. That's an arbitrary decision that's easy to account
  for in production code, and starting the timer first means that tests can
  easily rig the sender to cause its own timeout, for easy timeout test
  scenarios.

Notes:
- Compare: `incite_on`. `timeout_after` is similar in this respect.
  • Loading branch information
elbeno committed Dec 4, 2024
1 parent 846d895 commit 1a61428
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 10 deletions.
7 changes: 3 additions & 4 deletions include/async/when_any.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,9 @@ template <stdx::ct_string Name, typename Sndr> struct pipeable {
private:
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
return sender<Name, first_complete,
sub_sender<std::remove_cvref_t<S>, 0>,
sub_sender<Sndr, 1>>{std::forward<S>(s),
std::forward<Self>(self).sndr};
return sender<Name, first_complete, sub_sender<Sndr, 0>,
sub_sender<std::remove_cvref_t<S>, 1>>{
std::forward<Self>(self).sndr, std::forward<S>(s)};
}
};
} // namespace _when_any
Expand Down
25 changes: 23 additions & 2 deletions test/timeout_after.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "detail/common.hpp"

#include <async/just.hpp>
#include <async/just_result_of.hpp>
#include <async/schedulers/time_scheduler.hpp>
#include <async/schedulers/timer_manager.hpp>
#include <async/start_on.hpp>
Expand Down Expand Up @@ -118,6 +119,26 @@ TEST_CASE("timeout_after is pipeable", "[timeout_after]") {
CHECK(async::timer_mgr::is_idle());
}

TEST_CASE("timeout_after can incite its own timeout", "[timeout_after]") {
current_time<default_domain, tp_t> = tp_t{};
int var{};
auto s =
async::start_on(async::time_scheduler{1s}, async::just_result_of([] {
current_time<default_domain, tp_t> = tp_t{3s};
CHECK(not async::timer_mgr::is_idle());
async::timer_mgr::service_task();
return 42;
}));
auto to = s | async::timeout_after(2s, 17);
auto op = async::connect(to, error_receiver{[&](int i) { var = i; }});
async::start(op);

current_time<default_domain, tp_t> = tp_t{1s};
async::timer_mgr::service_task();
CHECK(var == 17);
CHECK(async::timer_mgr::is_idle());
}

namespace {
struct alt_domain;
using alt_timer_manager_t = async::generic_timer_manager<hal<alt_domain>>;
Expand Down Expand Up @@ -169,8 +190,8 @@ TEST_CASE("timeout_after can time out on custom channel", "[timeout_after]") {
async::timeout_after<alt_domain, async::set_value_t>(s, 100ms, 1.0f);
static_assert(
std::same_as<async::completion_signatures_of_t<decltype(to)>,
async::completion_signatures<async::set_value_t(int),
async::set_value_t(float)>>);
async::completion_signatures<async::set_value_t(float),
async::set_value_t(int)>>);
}

TEST_CASE("timeout_after can complete with value after timeout",
Expand Down
8 changes: 4 additions & 4 deletions test/when_any.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ TEST_CASE("first_successful policy", "[when_any]") {

TEST_CASE("first_complete policy", "[when_any]") {
int value{};
auto s1 = async::just_stopped();
auto s2 = async::just(17);
auto s1 = async::just(17);
auto s2 = async::just_stopped();
auto w = async::stop_when(s1, s2);

auto op = async::connect(w, stopped_receiver{[&] {
Expand Down Expand Up @@ -247,7 +247,7 @@ TEST_CASE("stop_when is pipeable", "[when_any]") {
value = i;
}});
async::start(op);
CHECK(value == 42);
CHECK(value == 17);
}

TEST_CASE("stop_when is adaptor-pipeable", "[when_any]") {
Expand All @@ -258,7 +258,7 @@ TEST_CASE("stop_when is adaptor-pipeable", "[when_any]") {
value = i;
}});
async::start(op);
CHECK(value == 42);
CHECK(value == 17);
}

TEST_CASE("when_any with zero args never completes", "[when_any]") {
Expand Down

0 comments on commit 1a61428

Please sign in to comment.