Skip to content

Commit 6b83131

Browse files
authored
Merge pull request #2017 from ericniebler/fix-stdexec-task-handling-of-environments
fix how `stdexec::task` handles environments and queries
2 parents 6d1434f + a68675e commit 6b83131

File tree

2 files changed

+83
-24
lines changed

2 files changed

+83
-24
lines changed

include/stdexec/__detail/__task.hpp

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,13 @@ namespace STDEXEC
394394

395395
struct __opstate_base : private allocator_type
396396
{
397-
template <class _Env>
398-
constexpr explicit __opstate_base(task&& __task, _Env const & __env) noexcept
397+
template <class _Env, class _OwnEnv>
398+
constexpr explicit __opstate_base(task&& __task,
399+
_Env const & __env,
400+
_OwnEnv const & __own_env) noexcept
399401
: allocator_type(__mk_alloc(__env))
400402
, __sch_(__mk_sched(__env, __get_allocator()))
403+
, __env_(__mk_env(__env, __own_env))
401404
, __task_(static_cast<task&&>(__task))
402405
{
403406
auto& __promise = __task_.__coro_.promise();
@@ -434,19 +437,26 @@ namespace STDEXEC
434437
}
435438

436439
start_scheduler_type __sch_;
440+
_TaskEnv __env_;
437441
task __task_;
438442
__error_variant_t __errors_{__no_init};
439443
};
440444

445+
template <class _Env>
446+
struct __own_env_box
447+
{
448+
__own_env_t<_Env> __own_env_;
449+
};
450+
441451
template <class _ParentPromise>
442452
struct STDEXEC_ATTRIBUTE(empty_bases) __awaiter final
443-
: __opstate_base
453+
: __own_env_box<env_of_t<_ParentPromise>>
454+
, __opstate_base
444455
, __stop_callback_box_t<env_of_t<_ParentPromise>>
445456
{
446457
constexpr explicit __awaiter(task&& __task, _ParentPromise& __parent) noexcept
447-
: __opstate_base(static_cast<task&&>(__task), STDEXEC::get_env(__parent))
448-
, __own_env_(__mk_own_env(STDEXEC::get_env(__parent)))
449-
, __env_(__mk_env(STDEXEC::get_env(__parent), __own_env_))
458+
: __awaiter::__own_env_box{__mk_own_env(STDEXEC::get_env(__parent))}
459+
, __opstate_base(static_cast<task&&>(__task), STDEXEC::get_env(__parent), this->__own_env_)
450460
, __parent_(__parent)
451461
{}
452462

@@ -501,10 +511,8 @@ namespace STDEXEC
501511
return __parent_.unhandled_stopped();
502512
}
503513

504-
__own_env_t<_ParentPromise> __own_env_;
505-
_TaskEnv __env_;
506-
__std::coroutine_handle<> __continuation_;
507-
_ParentPromise& __parent_;
514+
__std::coroutine_handle<> __continuation_;
515+
_ParentPromise& __parent_;
508516
};
509517

510518
struct __attrs
@@ -542,21 +550,23 @@ namespace STDEXEC
542550

543551
////////////////////////////////////////////////////////////////////////////////////////
544552
// task<T,E>::__opstate
545-
template <class _Ty, class _Env>
553+
template <class _Ty, class _TaskEnv>
546554
template <class _Rcvr>
547-
struct STDEXEC_ATTRIBUTE(empty_bases) task<_Ty, _Env>::__opstate final
555+
struct STDEXEC_ATTRIBUTE(empty_bases) task<_Ty, _TaskEnv>::__opstate final
548556
: __rcvr_box<_Rcvr> // holds the receiver so that we can pass __opstate_base a reference to it
549-
, __opstate_base
557+
, __own_env_box<env_of_t<_Rcvr>>
550558
, __stop_callback_box_t<env_of_t<_Rcvr>>
559+
, __opstate_base
551560
{
552561
public:
553562
using operation_state_concept = operation_state_tag;
554563

555564
explicit __opstate(task&& __task, _Rcvr&& __rcvr) noexcept
556565
: __rcvr_box<_Rcvr>{static_cast<_Rcvr&&>(__rcvr)}
557-
, __opstate_base(static_cast<task&&>(__task), STDEXEC::get_env(this->__rcvr_))
558-
, __own_env_(__mk_own_env(STDEXEC::get_env(this->__rcvr_)))
559-
, __env_(__mk_env(STDEXEC::get_env(this->__rcvr_), __own_env_))
566+
, __opstate::__own_env_box{__mk_own_env(STDEXEC::get_env(this->__rcvr_))}
567+
, __opstate_base(static_cast<task&&>(__task),
568+
STDEXEC::get_env(this->__rcvr_),
569+
this->__own_env_)
560570
{}
561571

562572
void start() & noexcept
@@ -634,15 +644,12 @@ namespace STDEXEC
634644
STDEXEC::set_stopped(static_cast<_Rcvr&&>(this->__rcvr_));
635645
return std::noop_coroutine();
636646
}
637-
638-
__own_env_t<_Rcvr> __own_env_;
639-
_Env __env_;
640647
};
641648

642649
////////////////////////////////////////////////////////////////////////////////////////
643650
// task<T,E>::promise_type
644-
template <class _Ty, class _Env>
645-
struct task<_Ty, _Env>::__promise : __task::__promise_base<_Ty>
651+
template <class _Ty, class _TaskEnv>
652+
struct task<_Ty, _TaskEnv>::__promise : __task::__promise_base<_Ty>
646653
{
647654
__promise() noexcept = default;
648655

@@ -814,6 +821,16 @@ namespace STDEXEC
814821
}
815822
}
816823

824+
template <__forwarding_query _Query, class... _Args>
825+
requires __queryable_with<_TaskEnv, _Query, _Args...>
826+
[[nodiscard]]
827+
constexpr auto query(_Query __tag, _Args&&... __args) const
828+
noexcept(__nothrow_queryable_with<_TaskEnv, _Query, _Args...>)
829+
-> __query_result_t<_TaskEnv, _Query, _Args...>
830+
{
831+
return __query<_Query>()(__promise_->__state_->__env_, static_cast<_Args&&>(__args)...);
832+
}
833+
817834
__promise const * __promise_;
818835
};
819836

test/stdexec/types/test_task.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ namespace
7979
CHECK(i == 42);
8080
}
8181

82-
auto test_task_int_ref(int& i) -> ex::task<int&>
82+
auto test_task_int_ref(int &i) -> ex::task<int &>
8383
{
8484
CHECK(get_id() == 0);
8585
co_await ex::schedule(ex::inline_scheduler{});
@@ -90,7 +90,7 @@ namespace
9090
TEST_CASE("test task<int&>", "[types][task]")
9191
{
9292
int value = 42;
93-
auto t = test_task_int_ref(value) | ex::then([](int& i) { return std::ref(i); });
93+
auto t = test_task_int_ref(value) | ex::then([](int &i) { return std::ref(i); });
9494
auto [i] = ex::sync_wait(std::move(t)).value();
9595
STATIC_REQUIRE(std::same_as<decltype(i), std::reference_wrapper<int>>);
9696
CHECK(&i.get() == &value);
@@ -207,7 +207,7 @@ namespace
207207

208208
template <ex::__not_same_as<environment_type> _Env>
209209
requires ex::__callable<ex::get_scheduler_t, _Env const &>
210-
explicit test_env2(_Env const & other) noexcept
210+
explicit test_env2(_Env const &other) noexcept
211211
: sch(ex::get_scheduler(other))
212212
{}
213213

@@ -278,6 +278,48 @@ namespace
278278
CHECK(i == 84'000'042);
279279
}
280280

281+
struct my_env
282+
{
283+
template <class>
284+
using env_type = my_env;
285+
286+
template <class Env>
287+
requires std::invocable<ex::get_delegation_scheduler_t, Env const &>
288+
&& std::same_as<std::invoke_result_t<ex::get_delegation_scheduler_t, Env const &>,
289+
ex::run_loop::scheduler>
290+
explicit my_env(Env const &env) noexcept
291+
: delegation_scheduler_(ex::get_delegation_scheduler(env))
292+
{}
293+
294+
[[nodiscard]]
295+
auto query(ex::get_delegation_scheduler_t) const noexcept
296+
{
297+
return delegation_scheduler_;
298+
}
299+
300+
ex::run_loop::scheduler delegation_scheduler_;
301+
};
302+
303+
auto
304+
test_task_provides_additional_queries_with_a_custom_env(ex::run_loop::scheduler sync_wt_dlgtn_sch)
305+
-> ex::task<int, my_env>
306+
{
307+
// Fetch sync_wait's run_loop scheduler from the environment.
308+
ex::run_loop::scheduler tsk_dlgtn_sch = co_await ex::read_env(ex::get_delegation_scheduler);
309+
CHECK(tsk_dlgtn_sch == sync_wt_dlgtn_sch);
310+
co_return 13;
311+
}
312+
313+
TEST_CASE("task can provide additional queries through a custom environment", "[types][task]")
314+
{
315+
ex::sync_wait(ex::let_value(ex::read_env(ex::get_delegation_scheduler),
316+
[](ex::run_loop::scheduler sync_wt_dlgtn_sch)
317+
{
318+
return test_task_provides_additional_queries_with_a_custom_env(
319+
sync_wt_dlgtn_sch);
320+
}));
321+
}
322+
281323
// FUTURE TODO: add support so that `co_await sndr` can return a reference.
282324
// auto test_task_awaits_just_ref_sender() -> ex::task<void> {
283325
// int value = 42;

0 commit comments

Comments
 (0)