369 lines
16 KiB
Markdown
369 lines
16 KiB
Markdown
[exec.consumers]
|
||
|
||
# 33 Execution control library [[exec]](./#exec)
|
||
|
||
## 33.9 Senders [[exec.snd]](exec.snd#exec.consumers)
|
||
|
||
### 33.9.13 Sender consumers [exec.consumers]
|
||
|
||
#### [33.9.13.1](#exec.sync.wait) this_thread::sync_wait [[exec.sync.wait]](exec.sync.wait)
|
||
|
||
[1](#exec.sync.wait-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5595)
|
||
|
||
this_thread::sync_wait and this_thread::sync_wait_with_variant are used
|
||
to block the current thread of execution
|
||
until the specified sender completes and
|
||
to return its async result[.](#exec.sync.wait-1.sentence-1)
|
||
|
||
sync_wait mandates
|
||
that the input sender has exactly one value completion signature[.](#exec.sync.wait-1.sentence-2)
|
||
|
||
[2](#exec.sync.wait-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5604)
|
||
|
||
Let *sync-wait-env* be the following exposition-only class type:namespace std::this_thread {struct *sync-wait-env* { execution::run_loop* *loop*; // *exposition only*auto query(execution::get_scheduler_t) const noexcept {return *loop*->get_scheduler(); }auto query(execution::get_delegation_scheduler_t) const noexcept {return *loop*->get_scheduler(); }};}
|
||
|
||
[3](#exec.sync.wait-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5622)
|
||
|
||
Let *sync-wait-result-type* and*sync-wait-with-variant-result-type* be exposition-only alias templates defined as follows:namespace std::this_thread {template<execution::[sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*, *decayed-tuple*,
|
||
type_identity_t>>; template<execution::[sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-with-variant-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*>>;}
|
||
|
||
[4](#exec.sync.wait-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5639)
|
||
|
||
The name this_thread::sync_wait denotes a customization point object[.](#exec.sync.wait-4.sentence-1)
|
||
|
||
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-4.sentence-2)
|
||
|
||
The expression this_thread::sync_wait(sndr) is expression-equivalent to the following,
|
||
except that sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait, sndr)*Mandates*:
|
||
|
||
- [(4.1)](#exec.sync.wait-4.1)
|
||
|
||
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait-4.1.sentence-1)
|
||
|
||
- [(4.2)](#exec.sync.wait-4.2)
|
||
|
||
The type *sync-wait-result-type*<Sndr> is well-formed[.](#exec.sync.wait-4.2.sentence-1)
|
||
|
||
- [(4.3)](#exec.sync.wait-4.3)
|
||
|
||
[same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<decltype(e), *sync-wait-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#exec.sync.wait-4.3.sentence-1)
|
||
|
||
[5](#exec.sync.wait-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5659)
|
||
|
||
Let *sync-wait-state* and *sync-wait-receiver* be the following exposition-only class templates:namespace std::this_thread {template<class Sndr>struct *sync-wait-state* { // *exposition only* execution::run_loop *loop*; // *exposition only* exception_ptr *error*; // *exposition only**sync-wait-result-type*<Sndr> *result*; // *exposition only*}; template<class Sndr>struct *sync-wait-receiver* { // *exposition only*using receiver_concept = execution::receiver_t; *sync-wait-state*<Sndr>* *state*; // *exposition only*template<class... Args>void set_value(Args&&... args) && noexcept; template<class Error>void set_error(Error&& err) && noexcept; void set_stopped() && noexcept; *sync-wait-env* get_env() const noexcept { return {&*state*->*loop*}; }};}
|
||
|
||
[ð](#exec.sync.wait-itemdecl:1)
|
||
|
||
`template<class... Args>
|
||
void set_value(Args&&... args) && noexcept;
|
||
`
|
||
|
||
[6](#exec.sync.wait-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5695)
|
||
|
||
*Effects*: Equivalent to:try {*state*->*result*.emplace(std::forward<Args>(args)...);} catch (...) {*state*->*error* = current_exception();}*state*->*loop*.finish();
|
||
|
||
[ð](#exec.sync.wait-itemdecl:2)
|
||
|
||
`template<class Error>
|
||
void set_error(Error&& err) && noexcept;
|
||
`
|
||
|
||
[7](#exec.sync.wait-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5714)
|
||
|
||
*Effects*: Equivalent to:*state*->*error* = *AS-EXCEPT-PTR*(std::forward<Error>(err)); // see [[exec.general]](exec.general "33.1 General")*state*->*loop*.finish();
|
||
|
||
[ð](#exec.sync.wait-itemdecl:3)
|
||
|
||
`void set_stopped() && noexcept;
|
||
`
|
||
|
||
[8](#exec.sync.wait-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5728)
|
||
|
||
*Effects*: Equivalent to *state*->*loop*.finish()[.](#exec.sync.wait-8.sentence-1)
|
||
|
||
[9](#exec.sync.wait-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5733)
|
||
|
||
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-9.sentence-1)
|
||
|
||
If [sender_to](exec.snd.concepts#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *sync-wait-receiver*<Sndr>> is false,
|
||
the expression sync_wait.apply_sender(sndr) is ill-formed;
|
||
otherwise, it is equivalent to:*sync-wait-state*<Sndr> state;auto op = connect(sndr, *sync-wait-receiver*<Sndr>{&state});
|
||
start(op);
|
||
|
||
state.*loop*.run();if (state.*error*) { rethrow_exception(std::move(state.*error*));}return std::move(state.*result*);
|
||
|
||
[10](#exec.sync.wait-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5751)
|
||
|
||
The behavior of this_thread::sync_wait(sndr) is undefined unless:
|
||
|
||
- [(10.1)](#exec.sync.wait-10.1)
|
||
|
||
It blocks the current thread of execution ([[defns.block]](defns.block "3.6 block"))
|
||
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3 Forward progress"))
|
||
until the specified sender completes[.](#exec.sync.wait-10.1.sentence-1)
|
||
[*Note [1](#exec.sync.wait-note-1)*:
|
||
The default implementation of sync_wait achieves
|
||
forward progress guarantee delegation by providing a run_loop scheduler
|
||
via the get_delegation_scheduler query
|
||
on the *sync-wait-receiver*'s environment[.](#exec.sync.wait-10.1.sentence-2)
|
||
The run_loop is driven by the current thread of execution[.](#exec.sync.wait-10.1.sentence-3)
|
||
â *end note*]
|
||
|
||
- [(10.2)](#exec.sync.wait-10.2)
|
||
|
||
It returns the specified sender's async results as follows:
|
||
* [(10.2.1)](#exec.sync.wait-10.2.1)
|
||
|
||
For a value completion,
|
||
the result datums are returned in
|
||
a tuple in an engaged optional object[.](#exec.sync.wait-10.2.1.sentence-1)
|
||
|
||
* [(10.2.2)](#exec.sync.wait-10.2.2)
|
||
|
||
For an error completion, an exception is thrown[.](#exec.sync.wait-10.2.2.sentence-1)
|
||
|
||
* [(10.2.3)](#exec.sync.wait-10.2.3)
|
||
|
||
For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait-10.2.3.sentence-1)
|
||
|
||
#### [33.9.13.2](#exec.sync.wait.var) this_thread::sync_wait_with_variant [[exec.sync.wait.var]](exec.sync.wait.var)
|
||
|
||
[1](#exec.sync.wait.var-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5781)
|
||
|
||
The name this_thread::sync_wait_with_variant denotes
|
||
a customization point object[.](#exec.sync.wait.var-1.sentence-1)
|
||
|
||
For a subexpression sndr,
|
||
let Sndr be decltype(into_variant(sndr))[.](#exec.sync.wait.var-1.sentence-2)
|
||
|
||
The expression this_thread::sync_wait_with_variant(sndr) is expression-equivalent to the following,
|
||
except sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait_with_variant, sndr)*Mandates*:
|
||
|
||
- [(1.1)](#exec.sync.wait.var-1.1)
|
||
|
||
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait.var-1.1.sentence-1)
|
||
|
||
- [(1.2)](#exec.sync.wait.var-1.2)
|
||
|
||
The type *sync-wait-with-variant-result-type*<Sndr> is well-formed[.](#exec.sync.wait.var-1.2.sentence-1)
|
||
|
||
- [(1.3)](#exec.sync.wait.var-1.3)
|
||
|
||
[same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<decltype(e), *sync-wait-with-variant-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#exec.sync.wait.var-1.3.sentence-1)
|
||
|
||
[2](#exec.sync.wait.var-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5804)
|
||
|
||
The expression sync_wait_with_variant.apply_sender(sndr) is equivalent to:using result_type = *sync-wait-with-variant-result-type*<Sndr>;if (auto opt_value = sync_wait(into_variant(sndr))) {return result_type(std::move(get<0>(*opt_value)));}return result_type(nullopt);
|
||
|
||
[3](#exec.sync.wait.var-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5814)
|
||
|
||
The behavior of this_thread::sync_wait_with_variant(sndr) is undefined unless:
|
||
|
||
- [(3.1)](#exec.sync.wait.var-3.1)
|
||
|
||
It blocks the current thread of execution ([[defns.block]](defns.block "3.6 block"))
|
||
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3 Forward progress"))
|
||
until the specified sender completes[.](#exec.sync.wait.var-3.1.sentence-1)
|
||
[*Note [1](#exec.sync.wait.var-note-1)*:
|
||
The default implementation of sync_wait_with_variant achieves
|
||
forward progress guarantee delegation by relying on
|
||
the forward progress guarantee delegation provided by sync_wait[.](#exec.sync.wait.var-3.1.sentence-2)
|
||
â *end note*]
|
||
|
||
- [(3.2)](#exec.sync.wait.var-3.2)
|
||
|
||
It returns the specified sender's async results as follows:
|
||
* [(3.2.1)](#exec.sync.wait.var-3.2.1)
|
||
|
||
For a value completion,
|
||
the result datums are returned in an engaged optional object
|
||
that contains a variant of tuples[.](#exec.sync.wait.var-3.2.1.sentence-1)
|
||
|
||
* [(3.2.2)](#exec.sync.wait.var-3.2.2)
|
||
|
||
For an error completion, an exception is thrown[.](#exec.sync.wait.var-3.2.2.sentence-1)
|
||
|
||
* [(3.2.3)](#exec.sync.wait.var-3.2.3)
|
||
|
||
For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait.var-3.2.3.sentence-1)
|
||
|
||
#### [33.9.13.3](#exec.spawn) execution::spawn [[exec.spawn]](exec.spawn)
|
||
|
||
[1](#exec.spawn-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5843)
|
||
|
||
spawn attempts to associate the given input sender with
|
||
the given token's async scope and, on success,
|
||
eagerly starts the input sender[.](#exec.spawn-1.sentence-1)
|
||
|
||
[2](#exec.spawn-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5848)
|
||
|
||
The name spawn denotes a customization point object[.](#exec.spawn-2.sentence-1)
|
||
|
||
For subexpressions sndr, token, and env,
|
||
|
||
- [(2.1)](#exec.spawn-2.1)
|
||
|
||
let Sndr be decltype((sndr)),
|
||
|
||
- [(2.2)](#exec.spawn-2.2)
|
||
|
||
let Token be remove_cvref_t<decltype((token))>, and
|
||
|
||
- [(2.3)](#exec.spawn-2.3)
|
||
|
||
let Env be remove_cvref_t<decltype((env))>[.](#exec.spawn-2.sentence-2)
|
||
|
||
If any of[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")<Env> are not satisfied,
|
||
the expression spawn(sndr, token, env) is ill-formed[.](#exec.spawn-2.sentence-3)
|
||
|
||
[3](#exec.spawn-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5863)
|
||
|
||
Let *spawn-state-base* be the exposition-only class:
|
||
|
||
[ð](#lib:execution::spawn-state-base)
|
||
|
||
namespace std::execution {struct *spawn-state-base* { // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};}
|
||
|
||
[4](#exec.spawn-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5875)
|
||
|
||
Let *spawn-receiver* be the exposition-only class:
|
||
|
||
[ð](#lib:execution::spawn-receiver)
|
||
|
||
namespace std::execution {struct *spawn-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-state-base** *state*; // *exposition only*void set_value() && noexcept { *state*->*complete*(); }void set_stopped() && noexcept { *state*->*complete*(); }};}
|
||
|
||
[5](#exec.spawn-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5891)
|
||
|
||
Let *spawn-state* be the exposition-only class template:
|
||
|
||
[ð](#lib:execution::spawn-state)
|
||
|
||
namespace std::execution {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *spawn-state* : *spawn-state-base* { // *exposition only*using *op-t* = connect_result_t<Sender, *spawn-receiver*>; // *exposition only**spawn-state*(Alloc alloc, Sender&& sndr, Token token); // *exposition only*void *complete*() noexcept override; // *exposition only*void *run*(); // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::template rebind_alloc<*spawn-state*>; *alloc-t* *alloc*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*void *destroy*() noexcept; // *exposition only*};}
|
||
|
||
[ð](#lib:execution::spawn-state,constructor)
|
||
|
||
`spawn-state(Alloc alloc, Sender&& sndr, Token token);
|
||
`
|
||
|
||
[6](#exec.spawn-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5924)
|
||
|
||
*Effects*: Initializes*alloc* with alloc,*token* with token, and*op* with:connect(std::move(sndr), *spawn-receiver*(this))
|
||
|
||
[ð](#lib:run,execution::spawn-state)
|
||
|
||
`void run();
|
||
`
|
||
|
||
[7](#exec.spawn-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5941)
|
||
|
||
*Effects*: Equivalent to:if (*token*.try_associate()) start(*op*);else*destroy*();
|
||
|
||
[ð](#lib:complete,execution::spawn-state)
|
||
|
||
`void complete() noexcept override;
|
||
`
|
||
|
||
[8](#exec.spawn-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5958)
|
||
|
||
*Effects*: Equivalent to:auto token = std::move(this->*token*);
|
||
|
||
*destroy*();
|
||
token.disassociate();
|
||
|
||
[ð](#lib:destroy,execution::spawn-state)
|
||
|
||
`void destroy() noexcept;
|
||
`
|
||
|
||
[9](#exec.spawn-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5975)
|
||
|
||
*Effects*: Equivalent to:auto alloc = std::move(this->*alloc*);
|
||
|
||
allocator_traits<*alloc-t*>::destroy(alloc, this);
|
||
allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);
|
||
|
||
[10](#exec.spawn-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5986)
|
||
|
||
For the expression spawn(sndr, token, env) let new_sender be the expression token.wrap(sndr) and
|
||
let alloc and senv be defined as follows:
|
||
|
||
- [(10.1)](#exec.spawn-10.1)
|
||
|
||
if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env,
|
||
|
||
- [(10.2)](#exec.spawn-10.2)
|
||
|
||
otherwise
|
||
if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression *JOIN-ENV*(prop(get_allocator, alloc), env),
|
||
|
||
- [(10.3)](#exec.spawn-10.3)
|
||
|
||
otherwisealloc is allocator<void>() andsenv is the expression env[.](#exec.spawn-10.sentence-1)
|
||
|
||
[11](#exec.spawn-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6006)
|
||
|
||
The expression spawn(sndr, token, env) is of type void and
|
||
has the following effects:
|
||
|
||
- [(11.1)](#exec.spawn-11.1)
|
||
|
||
Uses alloc to allocate and construct an object o of
|
||
type that is a specialization of *spawn-state* fromalloc, write_env(token.wrap(sndr), senv), and token and then
|
||
invokes o.*run*()[.](#exec.spawn-11.sentence-1)
|
||
If an exception is thrown then
|
||
any constructed objects are destroyed and any allocated memory is deallocated[.](#exec.spawn-11.1.sentence-2)
|
||
|
||
[12](#exec.spawn-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6021)
|
||
|
||
The expression spawn(sndr, token) is expression-equivalent tospawn(sndr, token, execution::env<>())[.](#exec.spawn-12.sentence-1)
|