From 6295f6665a85c39a46c494cf89c96325ddcff0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 24 Mar 2026 08:11:39 +0000 Subject: [PATCH 1/6] applied changes requested on 2026-03-23 --- docs/P3980-allocation.md | 87 +--------------------------------------- 1 file changed, 2 insertions(+), 85 deletions(-) diff --git a/docs/P3980-allocation.md b/docs/P3980-allocation.md index ee6279a..6cafe06 100644 --- a/docs/P3980-allocation.md +++ b/docs/P3980-allocation.md @@ -211,7 +211,7 @@ Change [task.promise] paragraphs 17 and 18: void* operator new(size_t size); ``` -[??]{.pnum} _Returns_: `operator new(size, allocator_arg, allocator());` +[??]{.pnum} _Effects_: equivalent to `operator new(size, allocator_arg, allocator_type());` ::: @@ -259,89 +259,6 @@ with an allocator equal to `palloc`. [20]{.pnum} _Returns_: A pointer to the allocated storage. -## Wording Change B: Fix type names, allow flexible position, use for env - -::: ednote - -Change [task.promise] paragraph 17 and 18 to use the correct type: - -::: - -``` -template - void* operator new(size_t size, Args&&... args); -``` - -[17]{.pnum} If there is no parameter with type `@[allocator_arg_t]{.rm}@@[const]{.add}@ @[allocator_arg_t&]{.add}@` -then let `alloc` be `allocator_type()`. Otherwise, let `arg_next` -be the parameter following the first `@[allocator_arg_t]{.rm}@@[const]{.add}@ @[allocator_arg_t&]{.add}@` parameter, -and let `alloc` be `allocator_type(arg_next)`. Let `PAlloc` -be `allocator_traits::template -rebind_alloc`, where `U` is an unspecified type whose size and -alignment are both `__STDCPP_DEFAULT_NEW_ALIGNMENT__`. - -[18]{.pnum} -_Mandates_: - -
    -
  • [18.1]{.pnum} The first parameter of type `@[allocator_arg_t]{.rm}@@[const]{.add}@ @[allocator_arg_t&]{.add}@` (if any) is not the last parameter.
  • -
  • [18.2]{.pnum} `allocator_type(arg_next)` is a valid expression if there is a parameter of type `allocator_arg_t`.
  • -
  • [18.3]{.pnum} `allocator_traits​::​pointer` is a pointer type.
  • -
- -[19]{.pnum} _Effects_: Initializes an allocator `palloc` of type -`PAlloc` with `alloc`. Uses `palloc` to allocate storage for the -smallest array of `U` sufficient to provide storage for a coroutine -state of size `size`, and unspecified additional state necessary to -ensure that `operator delete` can later deallocate this memory block -with an allocator equal to `palloc`. - -[20]{.pnum} _Returns_: A pointer to the allocated storage. - -## Wording Change C: Fix type names, allow flexible position, don't use for env - -::: ednote - -Change [task.promise] paragraph 17 and 18 to use the correct type and don't convert to `allocator_type`: - -::: - -``` -template - void* operator new(size_t size, Args&&... args); -``` - -[17]{.pnum} If there is no parameter with type `@[allocator_arg_t]{.rm}@@[const]{.add}@ @[allocator_arg_t&]{.add}@` -then let `alloc` be `@[allocator_type()]{.rm}@@[allocator()]{.add}@`. Otherwise, let `@[arg_next]{.rm}@@[alloc]{.add}@` -be the parameter following the first `@[allocator_arg_t]{.rm}@@[const]{.add}@ @[allocator_arg_t&]{.add}@` -parameter[, and let `alloc` be `allocator_type(arg_next)`]{.rm}. Let `PAlloc` -be `allocator_traits<@[allocator_type]{.rm}@@[remove_cvref_t]{.add}@>::template rebind_alloc`, where `U` is an unspecified type whose size and -alignment are both `__STDCPP_DEFAULT_NEW_ALIGNMENT__`. - -[18]{.pnum} -_Mandates_: - -
    -
  • [18.1]{.pnum} The first parameter of type `@[allocator_arg_t]{.rm}@@[const]{.add}@ @[allocator_arg_t&]{.add}@` (if any) is not the last parameter.
  • - -
  • -::: rm -[18.2]{.pnum} `allocator_type(arg_next)` is a valid expression if there is a parameter of type `allocator_arg_t`. -::: -
  • - -
  • [18.3]{.pnum} `allocator_traits​::​pointer` is a pointer type.
  • -
- -[19]{.pnum} _Effects_: Initializes an allocator `palloc` of type -`PAlloc` with `alloc`. Uses `palloc` to allocate storage for the -smallest array of `U` sufficient to provide storage for a coroutine -state of size `size`, and unspecified additional state necessary to -ensure that `operator delete` can later deallocate this memory block -with an allocator equal to `palloc`. - -[20]{.pnum} _Returns_: A pointer to the allocated storage. - # Use Allocator From Environment

@@ -387,7 +304,7 @@ template ::: add -[?]{.pnum} _Mandates_: `allocator_type(get_allocator(get_env(rcvr)))` is well-formed or `allocator_type()` is well-formed. +[?]{.pnum} _Mandates_: At least one of the espressions `allocator_type(get_allocator(get_env(rcvr)))` or `allocator_type()` is well-formed. ::: From c660d4b647ba7b0907b088f22a1f55a390bd17ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Tue, 24 Mar 2026 13:34:57 +0000 Subject: [PATCH 2/6] updated the paper number using P and update the date --- docs/P3980-allocation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/P3980-allocation.md b/docs/P3980-allocation.md index 6cafe06..0a6e9d3 100644 --- a/docs/P3980-allocation.md +++ b/docs/P3980-allocation.md @@ -1,7 +1,7 @@ --- title: Task's Allocator Use -document: D3980R0 -date: 2026-02-22 +document: P3980R1 +date: 2026-03-24 audience: - Library Evolution Working Group (LEWG) - Library Working Group (LWG) @@ -211,7 +211,7 @@ Change [task.promise] paragraphs 17 and 18: void* operator new(size_t size); ``` -[??]{.pnum} _Effects_: equivalent to `operator new(size, allocator_arg, allocator_type());` +[??]{.pnum} _Effects_: Equivalent to `return operator new(size, allocator_arg, allocator_type());` ::: @@ -304,7 +304,7 @@ template ::: add -[?]{.pnum} _Mandates_: At least one of the espressions `allocator_type(get_allocator(get_env(rcvr)))` or `allocator_type()` is well-formed. +[?]{.pnum} _Mandates_: At least one of the expressions `allocator_type(get_allocator(get_env(rcvr)))` and `allocator_type()` is well-formed. ::: From 382f466bd43b2f3d5f4284bdd2d55ffde766f3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 26 Mar 2026 01:19:56 +0000 Subject: [PATCH 3/6] applied requested wording changes --- docs/P3941-affinity.md | 431 +++++++++++++++++++++++++++++------------ 1 file changed, 302 insertions(+), 129 deletions(-) diff --git a/docs/P3941-affinity.md b/docs/P3941-affinity.md index 3fc1f2d..04dae86 100644 --- a/docs/P3941-affinity.md +++ b/docs/P3941-affinity.md @@ -1,7 +1,7 @@ --- title: Scheduler Affinity -document: P3941R3 -date: 2026-03-21 +document: D3941R4 +date: 2026-03-25 audience: - Concurrency Working Group (SG1) - Library Evolution Working Group (LEWG) @@ -31,6 +31,17 @@ meet its objective at run-time. # Change History +## R4 + +- remove the alternative wording using `get_scheduler` +- remove fallback `get_scheduler` for `get_start_scheduler` +- use `get_start_scheduler` instead of `get_scheduler` to refer to + the scheduler an operation got started on (either for queries or + for defining environments) +- use `start_scheduler_type` instead of `scheduler_type` in `task` +- give implementations permission to define `@_sndr_@.affine_on()` + `@_sndr_@.as_awaitable(@_p_@)` + ## R3 - rebase changes on the customization changes [P3826r3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3826r3.html) @@ -563,8 +574,8 @@ applied. ::: ::: ednote -If `get_start_scheduler` is introduced add it to the synopsis in -[execution.syn] after `get_scheduler` as follows: +Add `get_start_scheduler` to the synopsis in [execution.syn] after +`get_scheduler` as follows: ::: ``` @@ -598,8 +609,7 @@ namespace std::execution { ``` ::: ednote -If `get_start_scheduler` is introduced add a new section after -[exec.get.scheduler] as follows: +Add a new section after [exec.get.scheduler] as follows: ::: :::add @@ -612,15 +622,14 @@ an operation got started on. [2]{.pnum} The name `get_start_scheduler` denotes a query object. For a subexpression `env`, `get_start_scheduler(env)` is expression-equivalent to MANDATE-NOTHROW(AS-CONST(env).query(get_start_scheduler)) -if this expression is well-formed. Otherwise, `get_start_scheduler(env)` -is expression-equivalent to `get_scheduler(env)`. +if this expression is well-formed. Mandates: If the expression above is well-formed, its type satisfies `scheduler`. [3]{.pnum} `forwarding_query(execution::get_start_scheduler)` is a core constant expression and has value true. -[?]{.pnum} Given subexpressions `sndr` and `rcvr` such that +[4]{.pnum} Given subexpressions `sndr` and `rcvr` such that `sender_to` is `true` and the expression `get_start_scheduler(get_env(rcvr))` is well-formed, an operation state that is the result of calling `connect(sndr, rcvr)` shall, if @@ -630,9 +639,34 @@ scheduler `get_start_scheduler(get_env(rcvr))`. ::: ednote -If `get_start_scheduler` is introduced change -[[exec.snd.expos](https://wg21.link/exec.snd.expos)] paragraph 8 to have SCHED-ENV -use `get_start_scheduler` instead of `get_scheduler`: +Add two paragraphs at the start of [exec.snd.expos] giving +implementations permission to add members `affine_on` and `as_awaitable` +to standard library sender types: + +::: + +::: add + +[?]{.pnum} Given an expression `sndr`, whose type is any sender +type defined in the standard library, it is unspecified whether the +expression `sndr.affine_on()` is well-formed. If that expression +is well-formed, then the evaluation thereof shall satisfy the +semantic requirements of the `affine_on` [exec.affine.on] algorithm. + +[?]{.pnum} Given an expression `sndr`, whose type type is any sender type +defined in the standard library, and an expression `p`, whose type is a +promise type, it is unspecified whether the expression +`sndr.as_awaitable(p)` is well-formed. If that expression is +well-formed, then the evaluation thereof shall satisfy the semantic +requirements of the `as_awaitable` [exec.as.awaitable] algorithm. + +::: + +::: ednote + +Change [[exec.snd.expos](https://wg21.link/exec.snd.expos)] paragraph +8 to have SCHED-ENV use `get_start_scheduler` +instead of `get_scheduler`: ::: @@ -641,15 +675,16 @@ satisfies queryable such that `o2.query(@[get_scheduler]{.rm is a prvalue with the same type and value as `sch`, and such that `o2.query(get_domain)` is expression-equivalent to `sch.query(get_domain)`. - ::: ednote -The specification of `on` [[exec.on](https://wg21.link/exec.on)] shouldn't use `write_env` as it does, -i.e., this change removes these (not removing them was an oversight -in +The specification of `on` [[exec.on](https://wg21.link/exec.on)] +shouldn't use `write_env` as it does, i.e., this change removes +these (not removing them was an oversight in [P3826](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3826r3.html)). + In addition, if `get_start_scheduler` is introduced change how `on` -gets its scheduler in [[exec.on](https://wg21.link/exec.on)], i.e., change the use from -`get_scheduler` to use `get_start_scheduler`: +gets its scheduler in [[exec.on](https://wg21.link/exec.on)], i.e., +change the use from `get_scheduler` to use `get_start_scheduler`: + :::

@@ -695,7 +730,7 @@ to the operation state that results from connecting `out_sndr` with `out_rcvr`. Calling `start(op)` shall

  • [9.1]{.pnum} remember the current -scheduler, `get_@[start_]{.add}@scheduler(get_env(rcvr))`;
  • +scheduler, `@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr))`;
  • [9.2]{.pnum} start `sndr` on an execution agent belonging to `sch`'s associated execution resource;
  • [9.3]{.pnum} @@ -724,7 +759,7 @@ that is well-formed: `get_completion_scheduler(get_env(sndr))`
  • [10.1.2]{.pnum} -`get_@[start_]{.add}@scheduler(get_env(rcvr));` +`@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr));`
@@ -747,30 +782,33 @@ If any scheduling operation fails, an error completion on `out_rcvr` shall be ex

::: ednote -If `get_start_scheduler` is not introduced change [exec.get.scheduler] as follows: -::: -[1]{.pnum} `get_scheduler` asks a queryable object for its associated scheduler. +Change [[exec.sync.wait](https://wg21.link/exec.sync.wait#2)] p2 to also provide +a `get_start_scheduler` query: -[2]{.pnum} The name `get_scheduler` denotes a query object. For a -subexpression `env`, `get_scheduler(env)` is expression-equivalent to -MANDATE-NOTHROW(AS-CONST(env).query(get_scheduler)). +::: -Mandates: If the expression above is well-formed, its type satisfies `scheduler`. +Let `@_sync-wait-env_@` be the following exposition-only class type: -[3]{.pnum} `forwarding_query(execution::get_scheduler)` is a core -constant expression and has value true. +``` +namespace std::this_thread { + struct sync-wait-env { + execution::run_loop* loop; // @_exposition only_@ -:::add + auto query(execution::get_scheduler_t) const noexcept { + return loop->get_scheduler(); + } -[?]{.pnum} Given subexpressions `sndr` and `rcvr` such that -`sender_to` is `true` and the -expression `get_scheduler(get_env(rcvr))` is well-formed, an operation -state that is the result of calling `connect(sndr, rcvr)` shall, if -it is started, be started on an execution agent associated with the -scheduler `get_scheduler(get_env(rcvr))`. + @[auto]{.add}@ @[query(execution::get_start_scheduler_t)]{.add}@ @[const]{.add}@ @[noexcept]{.add}@ @[{]{.add}@ + @[return]{.add}@ @[loop->get_scheduler();]{.add}@ + @[}]{.add}@ -::: + auto query(execution::get_delegation_scheduler_t) const noexcept { + return loop->get_scheduler(); + } + }; +} +``` ::: ednote @@ -872,9 +910,8 @@ 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, ev); }) - return t.affine_on(child, ev); +if constexpr (requires{ child.affine_on(); }) + return child.affine_on(); else return continues_on(child, @_UNSTOPPABLE-SCHEDULER_@(get_start_scheduler(ev))); ``` @@ -925,94 +962,6 @@ 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 -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. If -`get_start_scheduler` is not introduced the algorithms should use -`get_scheduler` to get the start scheduler: -::: - -[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 -`@_make-sender_@(affine_on, @[sch]{.rm}@@[env<>()]{.add}@, sndr)`. - -:::{.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, -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, ev); }) - return t.affine_on(child, ev); -else - return continues_on(child, @_UNSTOPPABLE-SCHEDULER_@(get_scheduler(ev))); -``` - -[?]{.pnum} For a subexpression `sch` whose type satisfies `scheduler`, -let `@_UNSTOPPABLE-SCHEDULER_@(sch)` be an expression `e` whose type -satisfies `scheduler` such that: -
    -
  • [?.1]{.pnum} `schedule(e)` is expression-equivalent to `unstoppable(schedule(sch))`.
  • -
  • [?.2]{.pnum} For any query object `q` and pack of subexpressions `args...`, `e.query(q, args...)` -is expression-equivalent to `sch.query(q, args...)`.
  • -
  • [?.3]{.pnum} Let `f` be the subexpression `@_UNSTOPPABLE-SCHEDULER_@(other)`. `e == f` -is expression-equivalent to `sch == other`.
  • -
- -[?]{.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]: ::: @@ -1050,6 +999,153 @@ namespace std::execution { } ``` +::: ednot + +In [[task.class](https://wg21.link/task.class)] change `scheduler_type` to +`start_scheduler_type`: + +::: + +``` +namespace std::execution { + template> + class task { + // [task.state] + template + class @_state_@; // @_exposition only_@ + + public: + using sender_concept = sender_t; + using completion_signatures = @_see below_@; + using allocator_type = @_see below_@; + using @[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@ = @_see below_@; + using stop_source_type = @_see below_@; + using stop_token_type = decltype(declval().get_token()); + using error_types = @_see below_@; + + // [task.promise] + class promise_type; + + task(task&&) noexcept; + ~task(); + + template + @_state_@ connect(Rcvr&& rcvr) &&; + + private: + coroutine_handle handle; // @_exposition only_@ + }; +} +``` + +[1]{.pnum} +`task` models sender ([exec.snd]) if `T` is `void`, a reference +type, or a _cv_-unqualified non-array object type and `E` is a class +type. Otherwise a program that instantiates the definition of +`task` is ill-formed. + +[2]{.pnum} +The nested types of task template specializations are determined based on the `Environment` parameter: +
    +
  • [2.1]{.pnum} +`allocator_type` is `Environment::allocator_type` if that _qualified-id_ +is valid and denotes a type, `allocator` otherwise.
  • +
  • [2.2]{.pnum} +`@[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@` is `Environment::@[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@` if that _qualified-id_ + is valid and denotes a type, `task_scheduler` otherwise.
  • +
  • [2.3]{.pnum} +`stop_source_type` is `Environment::stop_source_type` if that +_qualified-id_ is valid and denotes a type, `inplace_stop_source` +otherwise.
  • +
  • [2.4]{.pnum} +`error_types` is `Environment::error_types` if that _qualified-id_ +is valid and denotes a type, +`completion_signatures` otherwise.
  • +
+ +[3]{.pnum} + +A program is ill-formed if `error_types` is not a specialization +of `execution::completion_signatures` or if the template arguments +of that specialization contain an element which is not of the form +`set_error_t(E)` for some type `E`. + +[4]{.pnum} +The type alias `completion_signatures` is a specialization of +`execution::completion_signatures` with the template arguments (in +unspecified order): + +
    +
  • [4.1]{.pnum} +`set_value_t()` if `T` is `void`, and `set_value_t(T)` otherwise;
  • +
  • [4.2]{.pnum} +template arguments of the specialization of +`execution::completion_signatures` denoted by `error_types`; and
  • +
  • [4.3]{.pnum} `set_stopped_t()`.
  • +
+[5]{.pnum} `allocator_type` shall meet the _Cpp17Allocator_ requirements. + +::: ednote +Change the scheduler propagation to `start` to use `get_start_scheduler` in [[task.state](https://wg21.link/task.state#4)] p4: +::: + +``` +void start() & noexcept; +``` + +[4]{.pnum} _Effects_: Let `@_prom_@` be the object `@_handle_@.promise()`. Associates `@_STATE_@(@_prom_@)`, `@_RCVR_@(@_prom_@)`, and `@_SCHED_@(@_prom_@)` with `*this` as follows: + +
    +
  • [4.1]{.pnum} STATE(prom) is *this.
  • +
  • [4.2]{.pnum} `@_RCVR_@(@_prom_@)` is `@_rcvr_@`.
  • +
  • [4.3]{.pnum} `@_SCHED_@(@_prom_@)` is the object initialized with +`@[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@(@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(@_rcvr_@)))` if that expression is +valid and `@[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@()` otherwise. If neither of these expressions +is valid, the program is ill-formed.
  • +
+ +Let `@_st_@` be `get_stop_token(get_env(@_rcvr_@))`. Initializes `@_prom_@.@_token_@` +and `@_prom.source_@` such that + +
    +
  • [4.4]{.pnum} +`@_prom_@.@_token_@.stop_requested()` returns `@_st_@.stop_requested()`;
  • +
  • [4.5]{.pnum} +`@_prom_@.@_token_@.stop_possible()` returns `@_st_@.stop_possible()`; and
  • +
  • [4.6]{.pnum} +for types `Fn` and `Init` such that both `invocable` and +`constructible_from` are modeled, +`stop_token_type::callback_type` models +`@_stoppable-callback-for_@`.
  • +
+ +After that invokes `@_handle_@.resume()`. + +::: ednote +Use `get_start_scheduler` instead of `get_scheduler` when providing +an environment to the `co_await`ed sender in +[[task.state](https://wg21.link/task.state#16)] p16: +::: + +``` +@_unspecified_@ get_env() const noexcept; +``` +[16]{.pnum} + +_Returns_: An object `env` such that queries are forwarded as follows: +
    +
  • [16.1]{.pnum} +`env.query(@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@)` returns `@[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@(@_SCHED_@(*this))`.
  • +
  • [16.2]{.pnum} +`env.query(get_allocator)` returns `@_alloc_@`.
  • +
  • [16.3]{.pnum} +`env.query(get_stop_token)` returns `@_token_@`.
  • +
  • [16.4]{.pnum} For any other query `q` and arguments `a...` a call +to `env.query(q, a...)` returns `STATE(*this).environment.query(q, +a...)` if this expression is well-formed and `forwarding_query(q)` is +well-formed and is `true`. Otherwise `env.query(q, a...)` is ill-formed.
  • +
+ ::: ednote Adjust the use of `affine_on` and remove `change_coroutine_scheduler` from [task.promise]: ::: @@ -1085,7 +1181,7 @@ 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)`. +_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 ``` @@ -1209,3 +1305,80 @@ valid until the end of the lifetime of its associated `run_loop` instance. ... + +::: ednote +Change +[[exec.counting.scopes.general](https://wg21.link/exec.counting.scopes.general#4)] +p4 to use `get_start_scheduler` instead of `get_scheduler`: +::: + +[4]{.pnum} The exposition-only class template `@_impls-for_@` ([exec.snd.expos]) is specialized for `@_scope-join-t_@` as follows: + +``` +namespace std::execution { + template<> + struct @_impls-for_@<@_scope-join-t_@> : @_default-impls_@ { + template + struct state { // @_exposition only_@ + struct @_rcvr-t_@ { // @_exposition only_@ + using receiver_concept = receiver_t; + + Rcvr& @_rcvr_@; // @_exposition only_@ + + void set_value() && noexcept { + execution::set_value(std::move(@_rcvr_@)); + } + + template + void set_error(E&& e) && noexcept { + execution::set_error(std::move(@_rcvr_@), std::forward(e)); + } + + void set_stopped() && noexcept { + execution::set_stopped(std::move(@_rcvr_@)); + } + + decltype(auto) get_env() const noexcept { + return execution::get_env(@_rcvr_@); + } + }; + + using @_sched-sender_@ = // @_exposition only_@ + decltype(schedule(@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(declval())))); + using @_op-t_@ = // @_exposition only_@ + connect_result_t<@_sched-sender_@, @_rcvr-t_@>; + + Scope* @_scope_@; // @_exposition only_@ + Rcvr& @_receiver_@; // @_exposition only_@ + @_op-t_@ @_op_@; // @_exposition only_@ + + @_state_@(Scope* scope, Rcvr& rcvr) // @_exposition only_@ + noexcept(@_nothrow-callable_@) + : @_scope_@(scope), + @_receiver_@(rcvr), + @_op_@(connect(schedule(@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr))), @_rcvr-t_@(rcvr))) {} + + void @_complete_@() noexcept { // @_exposition only_@ + start(@_op_@); + } + + void @_complete-inline_@() noexcept { // @_exposition only_@ + set_value(std::move(@_receiver_@)); + } + }; + + static constexpr auto @_get-state_@ = // @_exposition only_@ + [](auto&& sender, Rcvr& receiver) + noexcept(is_nothrow_constructible_v<@_state_@, @_data-type_@, Rcvr&>) { + auto[_, self] = sender; + return @_state_@(self, receiver); + }; + + static constexpr auto @_start_@ = // @_exposition only_@ + [](auto& s, auto&) noexcept { + if (s.@_scope_@->@_start-join-sender_@(s)) + s.@_complete-inline_@(); + }; + }; +} +``` From 6c5188e8311bd0137f747219cb21d703c9b6e986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Fri, 27 Mar 2026 08:11:55 +0000 Subject: [PATCH 4/6] added an infallible-scheduler concept --- docs/P3941-affinity.md | 197 ++++++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 90 deletions(-) diff --git a/docs/P3941-affinity.md b/docs/P3941-affinity.md index 04dae86..220fc29 100644 --- a/docs/P3941-affinity.md +++ b/docs/P3941-affinity.md @@ -40,7 +40,7 @@ meet its objective at run-time. for defining environments) - use `start_scheduler_type` instead of `scheduler_type` in `task` - give implementations permission to define `@_sndr_@.affine_on()` - `@_sndr_@.as_awaitable(@_p_@)` + and `@_sndr_@.as_awaitable(@_p_@)` ## R3 @@ -564,6 +564,23 @@ resolution in this issue is incomplete). The name `affine_on` isn't great. It may be worth giving the algorithm a better name. +## Use get_start_scheduler Properly + +During the LEWG discussion on 2026-03-24 it was brought up that +`get_start_scheduler` shouldn't fallback to `get_scheduler`. To +make proper use of `get_start_scheduler` without that fallback, +algorithms current using `get_scheduler` also need to use +`get_start_scheduler` and advertise it appropriately: + +- `let*` pass the first operation's `completion_scheduler` + as the `get_start_scheduler` for the second operation +- `starts_on` should advertise `get_start_scheduler` +- `task` should propagate `get_start_scheduler` instead of + `get_scheduler`. Since it names the type, the type is also + renamed `start_scheduler_type`. +- `sync_wait` advertises its scheduler using both `get_scheduler` + and `get_start_scheduler`. + # Wording Changes ::: ednote @@ -573,6 +590,43 @@ the changes from applied. ::: +::: ednote + +Add an exposition only concept `@_infallible-scheduler_@` to +[[exec.sched](https://wg21.link/exec.sched)]: + +::: + +... + +[7]{.pnum} A scheduler type's destructor shall not block pending completion of any receivers connected to the sender objects returned from `schedule`. + +::: add +[8]{.pnum} The exposition-only `@_infallible-scheduler_@` concept defines +the requirements of a scheduler type whose `schedule` asynchronous operation +can only complete with `set_value` unless stop is requested: + +``` +template +concept @_infallible-scheduler_@ = + scheduler && + (same_as, + completion_signatures_of_t())), Env> + > || + (!unstoppable_token> && ( + same_as, + completion_signatures_of_t())), Env> + > || + same_as, + completion_signatures_of_t())), Env> + >) + ) + ); +``` + +::: + + ::: ednote Add `get_start_scheduler` to the synopsis in [execution.syn] after `get_scheduler` as follows: @@ -617,12 +671,11 @@ Add a new section after [exec.get.scheduler] as follows: ### execution::get_start_scheduler [exec.get.start.scheduler] [1]{.pnum} `get_start_scheduler` asks a queryable object for the scheduler -an operation got started on. +an operation will be or was started on. [2]{.pnum} The name `get_start_scheduler` denotes a query object. For a subexpression `env`, `get_start_scheduler(env)` is expression-equivalent to -MANDATE-NOTHROW(AS-CONST(env).query(get_start_scheduler)) -if this expression is well-formed. +MANDATE-NOTHROW(AS-CONST(env).query(get_start_scheduler)). Mandates: If the expression above is well-formed, its type satisfies `scheduler`. @@ -650,14 +703,14 @@ to standard library sender types: [?]{.pnum} Given an expression `sndr`, whose type is any sender type defined in the standard library, it is unspecified whether the expression `sndr.affine_on()` is well-formed. If that expression -is well-formed, then the evaluation thereof shall satisfy the +is well-formed, then the evaluation thereof meets the semantic requirements of the `affine_on` [exec.affine.on] algorithm. -[?]{.pnum} Given an expression `sndr`, whose type type is any sender type +[?]{.pnum} Given an expression `sndr`, whose type is any sender type defined in the standard library, and an expression `p`, whose type is a promise type, it is unspecified whether the expression `sndr.as_awaitable(p)` is well-formed. If that expression is -well-formed, then the evaluation thereof shall satisfy the semantic +well-formed, then the evaluation thereof meets the semantic requirements of the `as_awaitable` [exec.as.awaitable] algorithm. ::: @@ -710,12 +763,10 @@ if constexpr (scheduler) { get_completion_scheduler, @_not-a-scheduler_@(), get_env(child), env); return continues_on( - @[write_env(]{.rm}@ std::forward_like(closure)( continues_on( - @[write_env(]{.rm}@std::forward_like(child)@[,]{.rm}@ @[_SCHED-ENV_(orig_sch))]{.rm}@, + std::forward_like(child), sch)), - @[_SCHED-ENV_(sch)),]{.rm}@ orig_sch); } ``` @@ -730,7 +781,7 @@ to the operation state that results from connecting `out_sndr` with `out_rcvr`. Calling `start(op)` shall
  • [9.1]{.pnum} remember the current -scheduler, `@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr))`;
  • +scheduler@[,]{.rm}@@[ which is obtained by]{.add}@ `@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr))`;
  • [9.2]{.pnum} start `sndr` on an execution agent belonging to `sch`'s associated execution resource;
  • [9.3]{.pnum} @@ -740,46 +791,6 @@ forward `sndr`'s async result to `out_rcvr`.
If any scheduling operation fails, an error completion on `out_rcvr` shall be executed on an unspecified execution agent.

-

-[10]{.pnum} -Let out_sndr be a subexpression denoting a sender returned from -`on(sndr, sch, closure)` 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` is `true`. Let `op` be an lvalue referring to -the operation state that results from connecting `out_sndr` with -`out_rcvr`. Calling `start(op)` shall -

-
    -
  • [10.1]{.pnum} remember -the current scheduler, which is the first of the following expressions -that is well-formed: -
      -
    • [10.1.1]{.pnum} -`get_completion_scheduler(get_env(sndr))` -
    • -
    • [10.1.2]{.pnum} -`@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr));` -
    • -
    -
  • -
  • [10.2]{.pnum} -start `sndr` on the current execution agent; -
  • -
  • [10.3]{.pnum} -upon `sndr`'s completion, transfer execution to an agent owned by sch's associated execution resource; -
  • -
  • [10.4]{.pnum} -forward `sndr`'s async result as if by connecting and starting a sender `closure(S)`, where `S` is a sender that completes synchronously with `sndr`'s async `result`; and -
  • -
  • [10.5]{.pnum} -upon completion of the operation started in the previous step, transfer execution back to the execution resource associated with the scheduler remembered in step 1 and forward the operation's async result to `out_rcvr`. -
  • -
- -

-If any scheduling operation fails, an error completion on `out_rcvr` shall be executed on an unspecified execution agent. -

::: ednote @@ -839,9 +850,12 @@ the type of the expression above. [7.?]{.pnum} -[`@_adapt-for-await-completion_@(transform_sender(expr, get_env(p))).as_awaitable(p)` -if this expression is well-formed, `sender_in>` is `true`, -and `@_single-sender-value-type_@>` is well-formed.]{.add} +[Otherwise, +`@_adapt-for-await-completion_@(transform_sender(expr, +get_env(p))).as_awaitable(p)` if this expression is well-formed, +`sender_in>` is `true`, and +`@_single-sender-value-type_@>` is well-formed, +except that `p` is only evaluated once.]{.add}
  • @@ -864,22 +878,29 @@ and `@_single-sender-value-type_@>` is well-formed.]{.ad
  • [7.4]{.pnum} Otherwise, [`@_sender-awaitable_@{expr, p}` if `@_awaitable-sender_@` is `true`.]{.rm} -[`@_sender-awaitable_@{@_adapt-for-await-completion_@(transform_sender(expr, get_env(p))), P}` if `sender_in>` is `true` and `@_single-sender-value-type_@>` is well-formed]{.add} +[`@_sender-awaitable_@{@_adapt-for-await-completion_@(transform_sender(expr, get_env(p))), p}` if `sender_in>` is `true` and `@_single-sender-value-type_@>` is well-formed], except that `p` is only evaluated once.{.add}
  • [7.5]{.pnum} Otherwise, `(void(p), expr)`.
  • -[8]{.pnum} [The expression `@_adapt-for-await-completion_@(s)` is -`get_await_completion_adaptor(get_env(s))(s)` if that is well-formed, -and `s` otherwise.]{.add} +::: add + +[8]{.pnum} `@_adapt-for-await-completion_@(s)` is expression-equivalent +to +
      +
    • `get_await_completion_adaptor(get_env(s))(s)` if that is well-formed, except that `s` is evaluated only once,
    • +
    • otherwise, `s`.
    • +
    + +::: ::: 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. If -`get_start_scheduler` is introduced the algorithms should use +which allows customization of `affine_on` for child senders. +The algorithm `affine_on` should use `get_start_scheduler` to get the start scheduler: ::: @@ -901,6 +922,18 @@ is expression-equivalent to `@_make-sender_@(affine_on, @[sch]{.rm}@@[env<>()]{.add}@, sndr)`. :::{.add} +[?]{.pnum} For a subexpression `sch` whose type models `scheduler`, +let `@_UNSTOPPABLE-SCHEDULER_@(sch)` be an expression `e` whose type +models `scheduler` such that: + +
      +
    • [?.1]{.pnum} `schedule(e)` is expression-equivalent to `unstoppable(schedule(sch))`.
    • +
    • [?.2]{.pnum} For any query object `q` and pack of subexpressions `args...`, `e.query(q, args...)` +is expression-equivalent to `sch.query(q, args...)`.
    • +
    • [?.3]{.pnum} The expression `e == @_UNSTOPPABLE-SCHEDULER_@(other)` +is expression-equivalent to `sch == other`.
    • +
    + [?]{.pnum} Let `sndr` and `ev` be subexpressions such that `Sndr` is `decltype((sndr))`. If sender-for<Sndr, @@ -910,26 +943,15 @@ it is equal to: ``` auto&[_, _, child] = sndr; -if constexpr (requires{ child.affine_on(); }) - return child.affine_on(); +if constexpr (requires{ std::forward_like(child).affine_on(); }) + return std::forward_like(child).affine_on(); else - return continues_on(child, @_UNSTOPPABLE-SCHEDULER_@(get_start_scheduler(ev))); + return continues_on(std::forward_like(child), @_UNSTOPPABLE-SCHEDULER_@(get_start_scheduler(ev))); ``` -[?]{.pnum} For a subexpression `sch` whose type satisfies `scheduler`, -let `@_UNSTOPPABLE-SCHEDULER_@(sch)` be an expression `e` whose type -satisfies `scheduler` such that: -
      -
    • [?.1]{.pnum} `schedule(e)` is expression-equivalent to `unstoppable(schedule(sch))`.
    • -
    • [?.2]{.pnum} For any query object `q` and pack of subexpressions `args...`, `e.query(q, args...)` -is expression-equivalent to `sch.query(q, args...)`.
    • -
    • [?.3]{.pnum} Let `f` be the subexpression `@_UNSTOPPABLE-SCHEDULER_@(other)`. `e == f` -is expression-equivalent to `sch == other`.
    • -
    - [?]{.pnum} _Recommended Practice_: Implementations should provide `affine_on` -member functions for senders which are known to resume on the +member functions for senders that 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`. @@ -940,16 +962,14 @@ and `write_env`. 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_ +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_start_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 +type `Env` [such that sender_in<_OutSndr_, Env> +is `true`]{.rm}. [If get_start_scheduler(get_env(out_rcvr)) is ill-formed +or does not satisfy `@_infallible-scheduler_@` then evaluation of the +expression `get_completion_signatures<@_OutSndr_@, Env>()` exits with +an exception. +]{.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 @@ -1096,7 +1116,7 @@ void start() & noexcept; [4]{.pnum} _Effects_: Let `@_prom_@` be the object `@_handle_@.promise()`. Associates `@_STATE_@(@_prom_@)`, `@_RCVR_@(@_prom_@)`, and `@_SCHED_@(@_prom_@)` with `*this` as follows:
      -
    • [4.1]{.pnum} STATE(prom) is *this.
    • +
    • [4.1]{.pnum} `@_STATE_@(prom)` is `*this`.
    • [4.2]{.pnum} `@_RCVR_@(@_prom_@)` is `@_rcvr_@`.
    • [4.3]{.pnum} `@_SCHED_@(@_prom_@)` is the object initialized with `@[scheduler_type]{.rm}@@[start_scheduler_type]{.add}@(@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(@_rcvr_@)))` if that expression is @@ -1216,11 +1236,8 @@ explicit task_scheduler(Sch&& sch, Allocator alloc = {}); ::: add [?]{.pnum} -_Mandates_: Let `E` be the type of a queryable. -If `unstoppable_token>` is `true`, then -the type `completion_signatures_of_t, E>` -only includes `set_value_t()`, otherwise it may additionally include -`set_stopped_t()`. +_Mandates_: `Sch` satisfies `@_infallible-scheduler_@>`. + ::: [2]{.pnum} From 88ea2d760008ca08ac399a295dac0286b70663c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 28 Mar 2026 08:37:51 +0000 Subject: [PATCH 5/6] added infallible scheduler and some fixes --- docs/P3941-affinity.md | 8 ++--- .../task/detail/infallible_scheduler.hpp | 33 +++++++++++++++++++ .../beman/task/detail/inline_scheduler.hpp | 2 ++ include/beman/task/detail/task_scheduler.hpp | 4 ++- tests/beman/task/task_scheduler.test.cpp | 5 +++ 5 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 include/beman/task/detail/infallible_scheduler.hpp diff --git a/docs/P3941-affinity.md b/docs/P3941-affinity.md index 220fc29..0316da9 100644 --- a/docs/P3941-affinity.md +++ b/docs/P3941-affinity.md @@ -1,7 +1,7 @@ --- title: Scheduler Affinity -document: D3941R4 -date: 2026-03-25 +document: P3941R4 +date: 2026-03-26 audience: - Concurrency Working Group (SG1) - Library Evolution Working Group (LEWG) @@ -604,7 +604,7 @@ Add an exposition only concept `@_infallible-scheduler_@` to ::: add [8]{.pnum} The exposition-only `@_infallible-scheduler_@` concept defines the requirements of a scheduler type whose `schedule` asynchronous operation -can only complete with `set_value` unless stop is requested: +can only complete with `set_value` unless stop can be requested: ``` template @@ -781,7 +781,7 @@ to the operation state that results from connecting `out_sndr` with `out_rcvr`. Calling `start(op)` shall
      • [9.1]{.pnum} remember the current -scheduler@[,]{.rm}@@[ which is obtained by]{.add}@ `@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr))`;
      • +scheduler[,]{.rm}[ which is obtained by]{.add} `@[get_scheduler]{.rm}@@[get_start_scheduler]{.add}@(get_env(rcvr))`;
      • [9.2]{.pnum} start `sndr` on an execution agent belonging to `sch`'s associated execution resource;
      • [9.3]{.pnum} diff --git a/include/beman/task/detail/infallible_scheduler.hpp b/include/beman/task/detail/infallible_scheduler.hpp new file mode 100644 index 0000000..40c1716 --- /dev/null +++ b/include/beman/task/detail/infallible_scheduler.hpp @@ -0,0 +1,33 @@ +// include/beman/task/detail/infallible_scheduler.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_INCLUDE_BEMAN_TASK_DETAIL_INFALLIBLE_SCHEDULER +#define INCLUDED_INCLUDE_BEMAN_TASK_DETAIL_INFALLIBLE_SCHEDULER + +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace beman::task::detail { +template +concept completes_with = + ::std::same_as<::beman::execution::completion_signatures, + ::beman::execution:: + completion_signatures_of_t())), Env>>; + +template +concept infallible_scheduler = + (::beman::execution::scheduler) && + (::beman::task::detail::completes_with || + (!::beman::execution::unstoppable_token<::beman::execution::stop_token_of_t> && + (::beman::task::detail:: + completes_with || + ::beman::task::detail:: + completes_with))); +} // namespace beman::task::detail + +// ---------------------------------------------------------------------------- + +#endif diff --git a/include/beman/task/detail/inline_scheduler.hpp b/include/beman/task/detail/inline_scheduler.hpp index 916db68..c6f4b11 100644 --- a/include/beman/task/detail/inline_scheduler.hpp +++ b/include/beman/task/detail/inline_scheduler.hpp @@ -5,6 +5,7 @@ #define INCLUDED_BEMAN_TASK_DETAIL_INLINE_SCHEDULER #include +#include #include #include @@ -60,6 +61,7 @@ struct inline_scheduler { bool operator==(const inline_scheduler&) const = default; }; static_assert(::beman::execution::scheduler); +static_assert(::beman::execution::scheduler<::beman::task::detail::inline_scheduler>); } // namespace beman::task::detail // ---------------------------------------------------------------------------- diff --git a/include/beman/task/detail/task_scheduler.hpp b/include/beman/task/detail/task_scheduler.hpp index 6bb83be..b5a5882 100644 --- a/include/beman/task/detail/task_scheduler.hpp +++ b/include/beman/task/detail/task_scheduler.hpp @@ -5,6 +5,7 @@ #define INCLUDED_BEMAN_TASK_DETAIL_task_scheduler #include +#include #include #include #include @@ -174,7 +175,8 @@ class task_scheduler { template > requires(not std::same_as>) && - ::beman::execution::scheduler<::std::remove_cvref_t> + ::beman::execution::scheduler<::std::remove_cvref_t> && + ::beman::task::detail::infallible_scheduler<::std::remove_cvref_t, ::beman::execution::env<>> explicit task_scheduler(S&& s, Allocator = {}) : scheduler(static_cast>*>(nullptr), std::forward(s)) {} task_scheduler(task_scheduler&&) = default; diff --git a/tests/beman/task/task_scheduler.test.cpp b/tests/beman/task/task_scheduler.test.cpp index 31e4a86..d5008a3 100644 --- a/tests/beman/task/task_scheduler.test.cpp +++ b/tests/beman/task/task_scheduler.test.cpp @@ -123,6 +123,10 @@ struct thread_context { struct sender { using sender_concept = ex::sender_t; using completion_signatures = ex::completion_signatures; + template + static consteval auto get_completion_signatures() -> completion_signatures { + return {}; + } thread_context* ctxt; thread_context::complete cmpl; @@ -149,6 +153,7 @@ struct thread_context { this->condition.notify_one(); } }; +static_assert(::beman::task::detail::infallible_scheduler>); enum class stop_result : char { none, success, failure, stopped }; template From 4e2566d22e904bbeb48f5318c67427f5bc06cebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 28 Mar 2026 08:41:47 +0000 Subject: [PATCH 6/6] fix CI issues --- docs/P3941-affinity.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/P3941-affinity.md b/docs/P3941-affinity.md index 0316da9..7e9d72f 100644 --- a/docs/P3941-affinity.md +++ b/docs/P3941-affinity.md @@ -568,7 +568,7 @@ algorithm a better name. During the LEWG discussion on 2026-03-24 it was brought up that `get_start_scheduler` shouldn't fallback to `get_scheduler`. To -make proper use of `get_start_scheduler` without that fallback, +make proper use of `get_start_scheduler` without that fallback, algorithms current using `get_scheduler` also need to use `get_start_scheduler` and advertise it appropriately: @@ -579,7 +579,7 @@ algorithms current using `get_scheduler` also need to use `get_scheduler`. Since it names the type, the type is also renamed `start_scheduler_type`. - `sync_wait` advertises its scheduler using both `get_scheduler` - and `get_start_scheduler`. + and `get_start_scheduler`. # Wording Changes @@ -612,11 +612,11 @@ concept @_infallible-scheduler_@ = scheduler && (same_as, completion_signatures_of_t())), Env> - > || + > || (!unstoppable_token> && ( same_as, completion_signatures_of_t())), Env> - > || + > || same_as, completion_signatures_of_t())), Env> >)