diff --git a/CMakeLists.txt b/CMakeLists.txt
index a1c58ec..88211db 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,7 +36,7 @@ FetchContent_Declare(
execution
# SOURCE_DIR ${CMAKE_SOURCE_DIR}/../execution
GIT_REPOSITORY https://github.com/bemanproject/execution
- GIT_TAG 7451ece
+ GIT_TAG 7d2a2b0
)
FetchContent_MakeAvailable(execution)
diff --git a/docs/P3941-affinity.md b/docs/P3941-affinity.md
index ea07814..b43e5ed 100644
--- a/docs/P3941-affinity.md
+++ b/docs/P3941-affinity.md
@@ -1,7 +1,7 @@
---
title: Scheduler Affinity
-document: P3941R0
-date: 2025-12-14
+document: P3941R1
+date: 2026-01-14
audience:
- Concurrency Working Group (SG1)
- Library Evolution Working Group (LEWG)
@@ -31,6 +31,10 @@ meet its objective at run-time.
# Change History
+## R1
+
+- added wording
+
## R0 Initial Revision
# Overview of Changes
@@ -495,6 +499,304 @@ resolution in this issue is incomplete).
The name `affine_on` isn't great. It may be worth giving the
algorithm a better name.
-# Wording Changes: TODO
+# Wording Changes
+
+::: ednote
+Change [exec.affine.on] to use only one parameter, require an
+infallible scheduler from the receiver, and add a default implementation
+which allows customization of `affine_on` for child senders:
+:::
+
+[1]{.pnum}
+`affine_on` adapts a sender into one that completes on a [specified
+scheduler]{.rm}[receiver's scheduler]{.add}. If the algorithm
+determines that the adapted sender already completes on the correct
+scheduler it can avoid any scheduling operation.
+
+[2]{.pnum}
+The name `affine_on` denotes a pipeable sender adaptor object. For
+[a ]{.add} subexpression[s sch and]{.rm} `sndr`, if [`decltype((sch))`
+does not satisfy scheduler, or]{.rm} `decltype((sndr))` does not
+satisfy sender, affine_on(sndr[, sch]{.rm}) is ill-formed.
+
+[3]{.pnum}
+Otherwise, the expression affine_on(sndr[, sch]{.rm})
+is expression-equivalent to:
+transform_sender(_get-domain-early_(sndr), _make-sender_(affine_on,
+[sch]{.rm}[env<>()]{.add}, sndr)) except that `sndr`
+is evaluated only once.
+
+[4]{.pnum}
+The exposition-only class template _impls-for_
+([exec.snd.expos]) is specialized for `affine_on_t` as follows:
+
+```c++
+namespace std::execution {
+ template<>
+ struct impls-for : default-impls {
+ static constexpr auto get-attrs =
+ [](const auto&@[ data]{.rm}]@, const auto& child) noexcept -> decltype(auto) {
+ return @[_JOIN-ENV_(_SCHED-ATTRS_(data),_FWD-ENV_(]{.rm}@get_env(child)@[))]{.rm}@;
+ };
+ };
+}
+```
+
+:::{.add}
+[?]{.pnum}
+Let `sndr` and `ev` be subexpressions such that `Sndr` is
+`decltype((sndr))`. If sender-for<Sndr,
+affine_on_t> is `false`, then the expression
+`affine_on.transform_sender(sndr, ev)` is ill-formed; otherwise,
+if otherwise, it is equal to:
+
+```
+auto&[_, _, child] = sndr;
+using child_tag_t = tag_of_t>;
+if constexpr (requires(const child_tag_t& t){ t.affine_on(child, env); })
+ return t.affine_on(child, env);
+else
+ return write_env(
+ schedule_from(get_scheduler(get_env(ev)), write_env(std::move(child), ev)),
+ JOIN-ENV(env{prop{get_stop_token, never_stop_token()}}, ev)
+ );
+```
+
+[Note 1: This causes the `affine_on(sndr)` sender to become
+`schedule_from(sch, sndr)` when it is connected with a receiver
+`rcvr` whose execution domain does not customize `affine_on`,
+for which `get_scheduler(get_env(rcvr))` is `sch`, and `affine_on`
+isn't specialized for the child sender.
+end note]
+
+[?]{.pnum}
+_Recommended Practice_: Implementations should provide `affine_on`
+member functions for senders which are known to resume on the
+scheduler where they were started. Example senders for which that
+is the case are `just`, `just_error`, `just_stopped`, `read_env`,
+and `write_env`.
+
+:::
+
+[5]{.pnum}
+Let _out_sndr_ be a subexpression denoting a sender
+returned from affine_on(sndr[, sch]{.rm}) or one equal
+to such, and let _OutSndr_ be the type
+decltype((_out_sndr_)). Let _out_rcvr_
+be a subexpression denoting a receiver that has an environment of
+type `Env` such that sender_in<_OutSndr_, Env>
+is `true`. [Let _sch_ be the result of the expression
+get_scheduler(get_env(_out_rcvr_)). If the completion
+signatures of schedule(_sch_) contain a different
+completion signature than `set_value_t()` when using an environment
+where `get_stop_token()` returns an `unstoppable_token`, the
+expression connect(out_sndr, out_rcvr) is
+ill-formed.]{.add} Let `op` be an lvalue referring to the operation
+state that results from connecting _out_sndr_ to
+_out_rcvr_. Calling start(_op_) will
+start `sndr` on the current execution agent and execute completion
+operations on _out_rcvr_ on an execution agent of the
+execution resource associated with [`sch`]{.rm}[_sch_]{.add}.
+If the current execution resource is the same as the execution
+resource associated with [`sch`]{.rm}[_sch_]{.add},
+the completion operation on _out_rcvr_ may be called
+before start(_op_) completes. [If scheduling onto `sch`
+fails, an error completion on _out_rcvr_ shall be
+executed on an unspecified execution agent.]{.rm}
+
+::: ednote
+Remove `change_coroutine_scheduler` from [execution.syn]:
+:::
+
+```
+namespace std::execution {
+ ...
+ // [exec.task.scheduler], task scheduler
+ class task_scheduler;
+
+ template
+ struct with_error {
+ using type = remove_cvref_t;
+ type error;
+ };
+ template
+ with_error(E) -> with_error;
+```
+::: rm
+```
+ template
+ struct change_coroutine_scheduler {
+ using type = remove_cvref_t;
+ type scheduler;
+ };
+ template
+ change_coroutine_scheduler(Sch) -> change_coroutine_scheduler;
+```
+:::
+```
+ // [exec.task], class template task
+ template
+ class task;
+ ...
+}
+```
+
+::: ednote
+Adjust the use of `affine_on` and remove `change_coroutine_scheduler` from [task.promise]:
+:::
+
+```
+namespace std::execution {
+ template
+ class task::promise_type {
+ public:
+ ...
+
+ template
+ auto await_transform(A&& a);
+```
+::: rm
+```
+ template
+ auto await_transform(change_coroutine_scheduler sch);
+```
+:::
+```
+
+ @_unspecified_@ get_env() const noexcept;
+
+ ...
+ }
+};
+```
+...
+
+```
+template
+ auto await_transform(Sender&& sndr) noexcept;
+```
+[9]{.pnum}
+_Returns_: If `same_as` is `true` returns `as_awaitable(std::forward(sndr), *this);` otherwise returns `as_awaitable(affine_on(std::forward(sndr)@[, SCHED(*this)]{.rm}@), *this)`.
+
+::: rm
+```
+template
+ auto await_transform(change_coroutine_scheduler sch) noexcept;
+```
+[10]{.pnum}
+_Effects_: Equivalent to:
+```
+return await_transform(just(exchange(SCHED(*this), scheduler_type(sch.scheduler))), *this);
+```
+:::
+
+```
+void unhandled_exception();
+```
+[11]{.pnum}
+_Effects_: If the signature `set_error_t(exception_ptr)` is not an element of `error_types`, calls `terminate()` ([except.terminate]). Otherwise, stores `current_exception()` into _errors_.
+
+...
+
+::: ednote
+In [exec.task.scheduler] change the constructor of `task_scheduler` to require that the scheduler passed
+is infallible
+:::
+
+```
+template>
+ requires(!same_as>) && scheduler
+explicit task_scheduler(Sch&& sch, Allocator alloc = {});
+```
+
+::: add
+[?]{.pnum}
+_Mandates_: Let `e` be an environment and let `E` be `decltype(e)`.
+If `unstoppable_token` is `true`, then
+the type `completion_signatures_of_t`
+only includes `set_value_t()`, otherwise it may additionally include
+`set_stopped_t()`.
+:::
+
+[2]{.pnum}
+_Effects_: Initialize sch_ with `allocate_shared>(alloc, std::forward(sch))`.
+
+[3]{.pnum}
+_Recommended practice_: Implementations should avoid the use of
+dynamically allocated memory for small scheduler objects.
+
+[4]{.pnum}
+_Remarks_: Any allocations performed by construction of
+_ts-sender_ or _state_ objects resulting
+from calls on `*this` are performed using a copy of `alloc`.
+
+::: ednote
+In [exec.task.scheduler] change the ts-sender completion signatures
+to indicate that `task_scheduler` is infallible:
+:::
+
+[8]{.pnum}
+```
+namespace std::execution {
+ class task_scheduler::@_ts-sender_@ { // @_exposition only_@
+ public:
+ using sender_concept = sender_t;
+
+ template
+ @_state_@ connect(Rcvr&& rcvr) &&;
+ };
+}
+```
+
+ts-sender is an exposition-only class that
+models `sender` ([exec.snd]) and for which
+completion_signatures_of_t<ts-sender[, E]{.add}>
+denotes[:]{.rm}[ `completion_signatures` if `unstoppable_token()))>` is `true`, and
+otherwise `completion_signatures`.]{.add}
+
+::: rm
+```
+completion_signatures<
+ set_value_t(),
+ set_error_t(error_code),
+ set_error_t(exception_ptr),
+ set_stopped_t()>
+```
+:::
+
+::: ednote
+In [exec.run.loop.types] change the paragraph defining the completion signatures:
+:::
+
+...
+
+```
+class run-loop-sender;
+```
+
+[5]{.pnum}
+run-loop-sender is an exposition-only type that satisfies `sender`.
+[Let `E` be the type of an environment. If `unstoppable_token()))>` is `true`,
+then ]{.add} completion_signatures_of_t<run-loop-sender[, E]{.add}> is
+
+::: rm
+```
+ completion_signatures`
+```
+:::
+
+::: add
+```
+ completion_signatures
+```
+Otherwise it is
+```
+ completion_signatures
+```
+:::
+
+[6]{.pnum} An instance of run-loop-sender remains
+valid until the end of the lifetime of its associated `run_loop`
+instance.
-To be done.
+...
\ No newline at end of file
diff --git a/examples/bulk.cpp b/examples/bulk.cpp
index b5f0c16..338e400 100644
--- a/examples/bulk.cpp
+++ b/examples/bulk.cpp
@@ -25,5 +25,5 @@ int main() {
ex::sync_wait(ex::write_env(ex::bulk(ex::just(), 16u, work{}), env{}));
ex::sync_wait(
- ex::write_env([]() -> ex::task { co_await ex::bulk(ex::just(), 16u, work{}); }(), env{}));
+ ex::write_env([]() -> ex::task> { co_await ex::bulk(ex::just(), 16u, work{}); }(), env{}));
}
diff --git a/examples/c++now-errors.cpp b/examples/c++now-errors.cpp
index c07d40f..f4fb468 100644
--- a/examples/c++now-errors.cpp
+++ b/examples/c++now-errors.cpp
@@ -36,8 +36,8 @@ using identity_or_none_t = typename identity_or_none::type;
#if 202202 <= __cpp_lib_expected
template
auto as_expected(Sender&& sndr) {
- using value_type = ex::value_types_of_t;
- using error_type = ex::error_types_of_t;
+ using value_type = ex::value_types_of_t, std::tuple, identity_or_none_t>;
+ using error_type = ex::error_types_of_t, identity_or_none_t>;
using result_type = std::expected;
return std::forward(sndr) |
diff --git a/examples/dangling-references.cpp b/examples/dangling-references.cpp
index e844623..501ba67 100644
--- a/examples/dangling-references.cpp
+++ b/examples/dangling-references.cpp
@@ -12,8 +12,8 @@ namespace ex = beman::execution;
// ----------------------------------------------------------------------------
namespace {
-ex::task do_work(std::string) { /* work */ co_return 0; };
-ex::task execute_all() {
+ex::task> do_work(std::string) { /* work */ co_return 0; };
+ex::task> execute_all() {
co_await ex::when_all(do_work("arguments 1"), do_work("arguments 2"));
co_return;
}
@@ -27,8 +27,8 @@ int main() {
ex::sync_wait([]() -> ex::task, error_env> { co_return ex::with_error{42}; }());
ex::sync_wait(execute_all());
- ex::sync_wait([]() -> ex::task {
- auto t = [](const int /* this would be added: &*/ v) -> ex::task { co_return v; }(42);
+ ex::sync_wait([]() -> ex::task> {
+ auto t = [](const int /* this would be added: &*/ v) -> ex::task> { co_return v; }(42);
[[maybe_unused]] auto v = co_await std::move(t);
}());
}
diff --git a/examples/issue-start-reschedules.cpp b/examples/issue-start-reschedules.cpp
index 89d721a..4d2a1e9 100644
--- a/examples/issue-start-reschedules.cpp
+++ b/examples/issue-start-reschedules.cpp
@@ -13,7 +13,7 @@ ex::task<> test(auto sched) {
std::cout << "init =" << std::this_thread::get_id() << "\n";
co_await ex::starts_on(sched, ex::just());
// static_assert(std::same_as);
+ // ex::env<>{}))>);
co_await ex::just();
std::cout << "final=" << std::this_thread::get_id() << "\n";
}
diff --git a/examples/rvalue-task.cpp b/examples/rvalue-task.cpp
index ed80988..a7fbc79 100644
--- a/examples/rvalue-task.cpp
+++ b/examples/rvalue-task.cpp
@@ -32,7 +32,7 @@ void test(T&& task) {
} // namespace
int main() {
- auto task = []() -> ex::task { co_return; }();
+ auto task = []() -> ex::task> { co_return; }();
test(std::move(task));
// test(task);
}
diff --git a/include/beman/task/detail/affine_on.hpp b/include/beman/task/detail/affine_on.hpp
deleted file mode 100644
index 14de6d2..0000000
--- a/include/beman/task/detail/affine_on.hpp
+++ /dev/null
@@ -1,184 +0,0 @@
-// include/beman/task/detail/affine_on.hpp -*-C++-*-
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#ifndef INCLUDED_INCLUDE_BEMAN_TASK_DETAIL_AFFINE_ON
-#define INCLUDED_INCLUDE_BEMAN_TASK_DETAIL_AFFINE_ON
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// ----------------------------------------------------------------------------
-
-namespace beman::task::detail {
-struct affine_on_t {
- template <::beman::execution::sender Sender>
- struct sender;
-
- template <::beman::execution::sender Sender>
- auto operator()(Sender&& sndr) const {
- using result_t = sender<::std::remove_cvref_t>;
- static_assert(::beman::execution::sender);
- return result_t(::std::forward(sndr));
- }
-};
-
-template <::beman::execution::sender Sender>
-struct affine_on_t::sender : ::beman::execution::detail::product_type<::beman::task::detail::affine_on_t, Sender> {
- using sender_concept = ::beman::execution::sender_t;
-
- template
- static constexpr bool elide_schedule =
- ::std::same_as<::beman::task::detail::inline_scheduler, ::std::remove_cvref_t>;
-
- template
- auto get_completion_signatures(const Env& env) const& noexcept {
- return ::beman::execution::get_completion_signatures(::std::remove_cvref_t(this->template get<1>()),
- env);
- }
- template
- auto get_completion_signatures(const Env& env) && noexcept {
- return ::beman::execution::get_completion_signatures(
- ::std::remove_cvref_t(::std::move(this->template get<1>())), env);
- }
-
- template <::beman::execution::receiver Receiver>
- struct state {
- template
- struct to_tuple_t;
- template
- struct to_tuple_t {
- using type = ::std::tuple...>;
- };
- template
- struct to_variant_t;
- template
- struct to_variant_t<::beman::execution::completion_signatures> {
- using type = ::beman::execution::detail::meta::unique<::std::variant::type...>>;
- };
-
- using operation_state_concept = ::beman::execution::operation_state_t;
- using completion_signatures = decltype(::beman::execution::get_completion_signatures(
- ::std::declval(), ::beman::execution::get_env(::std::declval())));
- using value_type = typename to_variant_t::type;
-
- struct schedule_receiver {
- using receiver_concept = ::beman::execution::receiver_t;
- state* s;
- auto set_value() noexcept -> void {
- static_assert(::beman::execution::receiver);
- std::visit(
- [this](auto&& v) -> void {
- std::apply(
- [this](auto tag, auto&&... a) { tag(std::move(this->s->receiver), ::std::move(a)...); },
- v);
- },
- s->value);
- }
- auto get_env() const noexcept -> ::beman::execution::empty_env { /*-dk:TODO */ return {}; }
- };
-
- struct work_receiver {
- using receiver_concept = ::beman::execution::receiver_t;
- state* s;
- template
- auto set_value(T&&... args) noexcept -> void {
- static_assert(::beman::execution::receiver);
- this->s->value
- .template emplace<::std::tuple<::beman::execution::set_value_t, ::std::remove_cvref_t...>>(
- ::beman::execution::set_value, ::std::forward(args)...);
- this->s->sched_op_.start();
- }
- template
- auto set_error(E&& error) noexcept -> void {
- static_assert(::beman::execution::receiver);
- this->s->value
- .template emplace<::std::tuple<::beman::execution::set_error_t, ::std::remove_cvref_t>>(
- ::beman::execution::set_error, ::std::forward(error));
- this->s->sched_op_.start();
- }
- auto set_stopped(auto&&...) noexcept -> void {
- static_assert(::beman::execution::receiver);
- this->s->value.template emplace<::std::tuple<::beman::execution::set_stopped_t>>(
- ::beman::execution::set_stopped);
- this->s->sched_op_.start();
- }
- auto get_env() const noexcept -> decltype(::beman::execution::get_env(::std::declval())) {
- return ::beman::execution::get_env(this->s->receiver);
- }
- };
- using scheduler_t =
- decltype(::beman::execution::get_scheduler(::beman::execution::get_env(::std::declval())));
- using schedule_op = decltype(::beman::execution::connect(
- ::beman::execution::schedule(::std::declval()), ::std::declval()));
- using work_op =
- decltype(::beman::execution::connect(::std::declval(), ::std::declval()));
-
- ::std::remove_cvref_t receiver;
- value_type value;
- schedule_op sched_op_;
- work_op work_op_;
-
- template
- explicit state(S&& s, R&& r)
- : receiver(::std::forward(r)),
- sched_op_(::beman::execution::connect(::beman::execution::schedule(::beman::execution::get_scheduler(
- ::beman::execution::get_env(this->receiver))),
- schedule_receiver{this})),
- work_op_(::beman::execution::connect(::std::forward(s), work_receiver{this})) {
- static_assert(::beman::execution::operation_state);
- if constexpr (not ::std::same_as<
- ::beman::execution::completion_signatures<::beman::execution::set_value_t()>,
- decltype(::beman::execution::get_completion_signatures(
- ::beman::execution::schedule(
- ::beman::execution::get_scheduler(::beman::execution::get_env(this->receiver))),
- ::beman::execution::get_env(this->receiver)))>) {
- static_assert(std::same_asreceiver)))>);
- }
- static_assert(::std::same_as<::beman::execution::completion_signatures<::beman::execution::set_value_t()>,
- decltype(::beman::execution::get_completion_signatures(
- ::beman::execution::schedule(::beman::execution::get_scheduler(
- ::beman::execution::get_env(this->receiver))),
- ::beman::execution::get_env(this->receiver)))>);
- }
- auto start() & noexcept -> void { ::beman::execution::start(this->work_op_); }
- };
-
- template
- sender(S&& s)
- : ::beman::execution::detail::product_type<::beman::task::detail::affine_on_t, Sender>{
- {{::beman::task::detail::affine_on_t{}}, {Sender(::std::forward(s))}}} {}
-
- template <::beman::execution::receiver Receiver>
- auto connect(Receiver&& receiver) && {
- static_assert(::std::same_as<::beman::execution::completion_signatures<::beman::execution::set_value_t()>,
- decltype(::beman::execution::get_completion_signatures(
- ::beman::execution::schedule(
- ::beman::execution::get_scheduler(::beman::execution::get_env(receiver))),
- ::beman::execution::get_env(receiver)))>,
- "affine_on requires that the receiver's scheduler is infallible");
- if constexpr (elide_schedule) {
- return ::beman::execution::connect(::std::move(this->template get<1>()),
- ::std::forward(receiver));
- } else {
- return state(::std::move(this->template get<1>()), ::std::forward(receiver));
- }
- }
-};
-} // namespace beman::task::detail
-
-namespace beman::task {
-using affine_on_t = ::beman::task::detail::affine_on_t;
-inline constexpr ::beman::task::detail::affine_on_t affine_on{};
-} // namespace beman::task
-
-// ----------------------------------------------------------------------------
-
-#endif
diff --git a/include/beman/task/detail/promise_type.hpp b/include/beman/task/detail/promise_type.hpp
index cde9088..27467a9 100644
--- a/include/beman/task/detail/promise_type.hpp
+++ b/include/beman/task/detail/promise_type.hpp
@@ -5,7 +5,6 @@
#define INCLUDED_INCLUDE_BEMAN_TASK_DETAIL_PROMISE_TYPE
#include
-#include
#include
#include
#include
@@ -81,7 +80,8 @@ class promise_type
::beman::execution::read_env_t>) {
return ::beman::execution::as_awaitable(::std::forward(sender), *this);
} else {
- return ::beman::execution::as_awaitable(::beman::task::affine_on(::std::forward(sender)), *this);
+ return ::beman::execution::as_awaitable(::beman::execution::affine_on(::std::forward(sender)),
+ *this);
}
}
auto await_transform(::beman::task::detail::change_coroutine_scheduler c) {
diff --git a/include/beman/task/task.hpp b/include/beman/task/task.hpp
index c23ef98..262d327 100644
--- a/include/beman/task/task.hpp
+++ b/include/beman/task/task.hpp
@@ -4,7 +4,6 @@
#ifndef INCLUDED_INCLUDE_BEMAN_TASK_TASK
#define INCLUDED_INCLUDE_BEMAN_TASK_TASK
-#include
#include
#include
#include
@@ -16,7 +15,6 @@
// ----------------------------------------------------------------------------
namespace beman::task {
-using affine_on_t = ::beman::task::detail::affine_on_t;
template
using allocator_of_t = ::beman::task::detail::allocator_of_t;
template
@@ -34,8 +32,6 @@ using ::beman::task::detail::with_error;
} // namespace beman::task
namespace beman::execution {
-using affine_on_t = ::beman::task::detail::affine_on_t;
-using ::beman::task::affine_on;
template
using allocator_of_t = ::beman::task::detail::allocator_of_t;
template
diff --git a/tests/beman/task/CMakeLists.txt b/tests/beman/task/CMakeLists.txt
index 1d69bbe..4f6b901 100644
--- a/tests/beman/task/CMakeLists.txt
+++ b/tests/beman/task/CMakeLists.txt
@@ -4,7 +4,6 @@ list(
APPEND
task_tests
msvc-asan-issue
- affine_on
allocator_of
allocator_support
completion
diff --git a/tests/beman/task/affine_on.test.cpp b/tests/beman/task/affine_on.test.cpp
deleted file mode 100644
index 8161b32..0000000
--- a/tests/beman/task/affine_on.test.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-// tests/beman/task/affine_on.test.cpp -*-C++-*-
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include
-#include
-#include
-#include
-#ifdef NDEBUG
-#undef NDEBUG
-#endif
-#include
-
-namespace ex = beman::execution;
-
-// ----------------------------------------------------------------------------
-
-namespace {
-template
-struct receiver {
- using receiver_concept = ex::receiver_t;
-
- std::remove_cvref_t scheduler;
- auto get_env() const noexcept { return ex::detail::make_env(ex::get_scheduler, scheduler); }
- auto set_value(auto&&...) && noexcept -> void {}
-};
-
-template
-receiver(Scheduler&& sched) -> receiver;
-
-template
-struct test_scheduler {
- using scheduler_concept = ex::scheduler_t;
- struct sender {
- using sender_concept = ex::sender_t;
- template
- auto get_completion_signatures(Env&& env) const noexcept {
- if constexpr (ex::unstoppable_token)
- return ex::completion_signatures{};
- else
- return ex::completion_signatures{};
- }
- struct env {
- template
- auto query(ex::get_completion_scheduler_t) const noexcept -> test_scheduler {
- return {};
- }
- };
- auto get_env() const noexcept { return env{}; }
-
- template
- struct op_state {
- using operation_state_concept = ex::operation_state_t;
- std::remove_cvref_t rcvr;
- void start() & noexcept {
- static_assert(ex::operation_state);
- ex::set_value(std::move(this->rcvr));
- }
- };
- template
- auto connect(Rcvr&& rcvr) noexcept {
- static_assert(ex::sender);
- return op_state{std::forward(rcvr)};
- }
- };
-
- auto schedule() noexcept -> sender {
- static_assert(ex::scheduler>);
- return {};
- }
- auto operator==(const test_scheduler&) const -> bool = default;
-};
-static_assert(ex::scheduler>);
-
-static_assert(ex::receiver>);
-static_assert(ex::receiver>>);
-
-template
-auto test_affine_on_accepted_scheduler(Receiver rcvr) {
- if constexpr (requires { ex::connect(beman::task::affine_on(ex::just()), std::move(rcvr)); }) {
- auto state(ex::connect(beman::task::affine_on(ex::just()), std::move(rcvr)));
- ex::start(state);
- }
-}
-
-auto test_affine_on_accepted_scheduler() {
- test_affine_on_accepted_scheduler(receiver{beman::task::detail::inline_scheduler{}});
- test_affine_on_accepted_scheduler(receiver{test_scheduler<>{}});
- // test_affine_on_accepted_scheduler(receiver{test_scheduler{}});
-}
-
-} // namespace
-
-int main() {
- beman::task::detail::single_thread_context context;
-
- auto main_id{std::this_thread::get_id()};
- auto [thread_id]{
- ex::sync_wait(ex::schedule(context.get_scheduler()) | ex::then([] { return std::this_thread::get_id(); }))
- .value_or(std::tuple{std::thread::id{}})};
-
- assert(main_id != thread_id);
-
- [[maybe_unused]] auto s(beman::task::affine_on(ex::just(42)));
- static_assert(ex::sender);
- [[maybe_unused]] auto st(ex::connect(std::move(s), receiver{context.get_scheduler()}));
- ex::sync_wait(
- beman::task::affine_on(ex::starts_on(context.get_scheduler(), ex::just(42)) | ex::then([thread_id](int value) {
- assert(thread_id == std::this_thread::get_id());
- assert(value == 42);
- })) |
- ex::then([thread_id]() { assert(thread_id != std::this_thread::get_id()); }));
- test_affine_on_accepted_scheduler();
-}