-
Notifications
You must be signed in to change notification settings - Fork 206
Open
Description
Consider the following code using exec::task
:
exec::task<void> h1() {
std::cerr << "entered h1\n";
co_return;
}
exec::task<void> h2() {
co_await h1();
std::cerr << "left h1\n";
}
When h1() completes and h2() resumes, I expected execution to continue on the same execution resource that was running h1(). However, it looks like exec::task
reschedules onto its stored scheduler.
You can verify this by using a scheduler that prints a message when schedule() is called:
template <stdexec::scheduler BaseScheduler>
struct printing_scheduler {
BaseScheduler base_sched;
struct sender {
using sender_concept = stdexec::sender_t;
BaseScheduler base_sched;
using base_sender_t = stdexec::schedule_result_t<BaseScheduler>;
template <class Env>
auto get_completion_signatures(Env &&) const noexcept
-> stdexec::completion_signatures_of_t<base_sender_t, Env> {
return {};
}
template <class Receiver>
auto connect(Receiver &&rcvr) && {
return stdexec::connect(
stdexec::schedule(base_sched) | stdexec::then([] { std::cerr << "scheduled\n"; }),
static_cast<Receiver &&>(rcvr));
}
constexpr auto get_env() const noexcept {
return stdexec::env{
stdexec::prop{
stdexec::get_completion_scheduler<stdexec::set_value_t>, printing_scheduler{base_sched}},
stdexec::get_env(stdexec::schedule(base_sched))
};
}
};
auto schedule() const {
return sender{base_sched};
}
bool operator==(const printing_scheduler &other) const = default;
auto query(stdexec::get_forward_progress_guarantee_t q) const noexcept {
return q(base_sched);
}
};
static_assert(stdexec::scheduler<printing_scheduler<stdexec::inline_scheduler>>);
static_assert(stdexec::sender<printing_scheduler<stdexec::inline_scheduler>::sender>);
int main() {
auto ctx = exec::single_thread_context{};
stdexec::sync_wait(stdexec::starts_on(printing_scheduler{ctx.get_scheduler()}, h2()));
}
The output is:
scheduled
entered h1
scheduled <- here you can see it gets rescheduled
left h1
I tried to track down the cause. When co_awaiting another exec::task inside an exec::task, it appears to run the following code:
Lines 392 to 400 in bb13042
template <sender _Awaitable> | |
requires __scheduler_provider<_Context> | |
auto await_transform(_Awaitable&& __awaitable) noexcept -> decltype(auto) { | |
// TODO: If we have a complete-where-it-starts query then we can optimize | |
// this to avoid the reschedule | |
return stdexec::as_awaitable( | |
continues_on(static_cast<_Awaitable&&>(__awaitable), get_scheduler(*__context_)), | |
*this); | |
} |
I suspect the rescheduling happens because continues_on()
is used here.
Should the following overload be selected instead?
Lines 426 to 430 in bb13042
template <class _Awaitable> | |
auto await_transform(_Awaitable&& __awaitable) noexcept -> decltype(auto) { | |
return with_awaitable_senders<__promise>::await_transform( | |
static_cast<_Awaitable&&>(__awaitable)); | |
} |
Thank you for your time.
Metadata
Metadata
Assignees
Labels
No labels