[exec.task] # 33 Execution control library [[exec]](./#exec) ## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.task) ### 33.13.6 execution​::​task [exec.task] #### [33.13.6.1](#task.overview) task overview [[task.overview]](task.overview) [1](#task.overview-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7171) The task class template represents a sender that can be used as the return type of coroutines[.](#task.overview-1.sentence-1) The first template parameter T defines the type of the value completion datum ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) if T is not void[.](#task.overview-1.sentence-2) Otherwise, there are no value completion datums[.](#task.overview-1.sentence-3) Inside coroutines returning task the operand ofco_return (if any) becomes the argument of set_value[.](#task.overview-1.sentence-4) The second template parameter Environment is used to customize the behavior of task[.](#task.overview-1.sentence-5) #### [33.13.6.2](#task.class) Class template task [[task.class]](task.class) namespace std::execution {templateclass [task](#lib:task "33.13.6.2 Class template task [task.class]") {// [[task.state]](#task.state "33.13.6.4 Class template task​::​state")template<[receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") Rcvr>class *state*; // *exposition only*public:using sender_concept = sender_t; using completion_signatures = *see below*; using allocator_type = *see below*; using scheduler_type = *see below*; using stop_source_type = *see below*; using stop_token_type = decltype(declval().get_token()); using error_types = *see below*; // [[task.promise]](#task.promise "33.13.6.5 Class task​::​promise_­type")class promise_type; task(task&&) noexcept; ~task(); template<[receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") Rcvr>*state* connect(Rcvr&& rcvr); private: coroutine_handle *handle*; // *exposition only*};} [1](#task.class-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7216) task models [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") ([[exec.snd]](exec.snd "33.9 Senders")) if T is void, a reference type, or a cv-unqualified non-array object type and E is a class type[.](#task.class-1.sentence-1) Otherwise a program that instantiates the definition of task is ill-formed[.](#task.class-1.sentence-2) [2](#task.class-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7223) The nested types of task template specializations are determined based on the Environment parameter: - [(2.1)](#task.class-2.1) allocator_type is Environment​::​allocator_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") is valid and denotes a type,allocator otherwise[.](#task.class-2.1.sentence-1) - [(2.2)](#task.class-2.2) scheduler_type is Environment​::​scheduler_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") is valid and denotes a type,task_scheduler otherwise[.](#task.class-2.2.sentence-1) - [(2.3)](#task.class-2.3) stop_source_type is Environment​::​stop_source_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") is valid and denotes a type,inplace_stop_source otherwise[.](#task.class-2.3.sentence-1) - [(2.4)](#task.class-2.4) error_types is Environment​::​error_types if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") is valid and denotes a type,completion_signatures otherwise[.](#task.class-2.4.sentence-1) [3](#task.class-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7241) A program is ill-formed if error_types is not a specialization of completion_signatures orErrorSigs contains an element which is not of the formset_error_t(E) for some type E[.](#task.class-3.sentence-1) [4](#task.class-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7247) The type alias completion_signatures is a specialization of execution​::​completion_signatures with the template arguments (in unspecified order): - [(4.1)](#task.class-4.1) set_value_t() if T is void, and set_value_t(T) otherwise; - [(4.2)](#task.class-4.2) template arguments of the specialization ofexecution​::​completion_signatures denoted by error_types; and - [(4.3)](#task.class-4.3) set_stopped_t()[.](#task.class-4.sentence-1) [5](#task.class-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7260) allocator_type shall meet the *Cpp17Allocator* requirements[.](#task.class-5.sentence-1) #### [33.13.6.3](#task.members) task members [[task.members]](task.members) [🔗](#lib:task,constructor) `task(task&& other) noexcept; ` [1](#task.members-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7271) *Effects*: Initializes *handle* with exchange(other.*handle*,{})[.](#task.members-1.sentence-1) [🔗](#lib:task,destructor) `~task(); ` [2](#task.members-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7282) *Effects*: Equivalent to:if (*handle*)*handle*.destroy(); [🔗](#lib:connect,task) `template<[receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") Rcvr> state connect(Rcvr&& recv); ` [3](#task.members-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7297) *Preconditions*: bool(*handle*) is true[.](#task.members-3.sentence-1) [4](#task.members-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7301) *Effects*: Equivalent to:return *state*(exchange(*handle*, {}), std::forward(recv)); #### [33.13.6.4](#task.state) Class template task​::​*state* [[task.state]](task.state) namespace std::execution {templatetemplate<[receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") Rcvr>class task::*state* { // *exposition only*public:using operation_state_concept = operation_state_t; template*state*(coroutine_handle h, R&& rr); ~*state*(); void start() & noexcept; private:using *own-env-t* = *see below*; // *exposition only* coroutine_handle *handle*; // *exposition only* remove_cvref_t *rcvr*; // *exposition only**own-env-t* *own-env*; // *exposition only* Environment *environment*; // *exposition only*};} [1](#task.state-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7336) The type *own-env-t* is Environment​::​template env_type(​)))​> if that[*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") is valid and denotes a type, env<> otherwise[.](#task.state-1.sentence-1) [🔗](#lib:task::state,constructor) `template state(coroutine_handle h, R&& rr); ` [2](#task.state-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7347) *Effects*: Initializes - [(2.1)](#task.state-2.1) *handle* with std​::​move(h); - [(2.2)](#task.state-2.2) *rcvr* with std​::​forward(rr); - [(2.3)](#task.state-2.3) *own-env* with *own-env-t*(get_env(*rcvr*)) if that expression is valid and *own-env-t*() otherwise[.](#task.state-2.3.sentence-1) If neither of these expressions is valid, the program is ill-formed[.](#task.state-2.3.sentence-2) - [(2.4)](#task.state-2.4) *environment* withEnvironment(*own-env*) if that expression is valid, otherwise Environment(​get_env(*rcvr*)) if this expression is valid, otherwise Environment()[.](#task.state-2.4.sentence-1) If neither of these expressions is valid, the program is ill-formed[.](#task.state-2.4.sentence-2) [🔗](#lib:task::state,destructor) `~state(); ` [3](#task.state-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7370) *Effects*: Equivalent to:if (*handle*)*handle*.destroy(); [🔗](#lib:start,task::state) `void start() & noexcept; ` [4](#task.state-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7384) *Effects*: Let *prom* be the object *handle*.promise()[.](#task.state-4.sentence-1) Associates *STATE*(*prom*), *RCVR*(*prom*), and *SCHED*(*prom*) with *this as follows: - [(4.1)](#task.state-4.1) *STATE*(*prom*) is *this[.](#task.state-4.1.sentence-1) - [(4.2)](#task.state-4.2) *RCVR*(*prom*) is *rcvr*[.](#task.state-4.2.sentence-1) - [(4.3)](#task.state-4.3) *SCHED*(*prom*) is the object initialized with scheduler_type(get_scheduler(get_env(*rcvr*))) if that expression is valid and scheduler_type() otherwise[.](#task.state-4.3.sentence-1) If neither of these expressions is valid, the program is ill-formed[.](#task.state-4.3.sentence-2) Let *st* be get_stop_token(get_env(*rcvr*))[.](#task.state-4.sentence-3) Initializes *prom*.*token* and*prom*.*source* such that - [(4.4)](#task.state-4.4) *prom*.*token*.stop_requested() returns*st*.stop_requested(); - [(4.5)](#task.state-4.5) *prom*.*token*.stop_possible() returns*st*.stop_possible(); and - [(4.6)](#task.state-4.6) for types Fn and Init such that both[invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]") and[constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]") are modeled,stop_token_type​::​callback_type models[*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3 Stop token concepts [stoptoken.concepts]")[.](#task.state-4.sentence-4) After that invokes *handle*.resume()[.](#task.state-4.sentence-5) #### [33.13.6.5](#task.promise) Class task​::​promise_type [[task.promise]](task.promise) namespace std::execution {templateclass task::promise_type {public:template promise_type(const Args&... args); task get_return_object() noexcept; auto initial_suspend() noexcept; auto final_suspend() noexcept; void uncaught_exception(); coroutine_handle<> unhandled_stopped(); void return_void(); // present only if is_void_v is truetemplatevoid return_value(V&& value); // present only if is_void_v is falsetemplate*unspecified* yield_value(with_error error); templateauto await_transform(A&& a); templateauto await_transform(change_coroutine_scheduler sch); *unspecified* get_env() const noexcept; templatevoid* operator new(size_t size, Args&&... args); void operator delete(void* pointer, size_t size) noexcept; private:using *error-variant* = *see below*; // *exposition only* allocator_type *alloc*; // *exposition only* stop_source_type *source*; // *exposition only* stop_token_type *token*; // *exposition only* optional *result*; // *exposition only*; present only if is_void_v is false*error-variant* *errors*; // *exposition only*};} [1](#task.promise-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7466) Let *prom* be an object of promise_type and let *tsk* be the task object created by *prom*.get_return_object()[.](#task.promise-1.sentence-1) The description below refers to objects *STATE*(*prom*),*RCVR*(*prom*), and *SCHED*(*prom*) associated with *tsk* during evaluation of task​::​*state*​::​start for some receiver Rcvr[.](#task.promise-1.sentence-2) [2](#task.promise-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7478) *error-variant* is a variant...>, with duplicate types removed, where E... are the parameter types of the template arguments of the specialization ofexecution​::​completion_signatures denoted byerror_types[.](#task.promise-2.sentence-1) [🔗](#lib:task::promise_type,constructor) `template promise_type(const Args&... args); ` [3](#task.promise-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7491) *Mandates*: The first parameter of type allocator_arg_t (if any) is not the last parameter[.](#task.promise-3.sentence-1) [4](#task.promise-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7496) *Effects*: If Args contains an element of type allocator_arg_t then *alloc* is initialized with the corresponding next element of args[.](#task.promise-4.sentence-1) Otherwise, *alloc* is initialized with allocator_type()[.](#task.promise-4.sentence-2) [🔗](#lib:get_return_object,task::promise_type) `task get_return_object() noexcept; ` [5](#task.promise-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7509) *Returns*: A task object whose member *handle* iscoroutine_handle​::​​from_promise​(*this)[.](#task.promise-5.sentence-1) [🔗](#lib:initial_suspend,task::promise_type) `auto initial_suspend() noexcept; ` [6](#task.promise-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7520) *Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4 Await")) whose member functions arrange for - [(6.1)](#task.promise-6.1) the calling coroutine to be suspended, - [(6.2)](#task.promise-6.2) the coroutine to be resumed on an execution agent of the execution resource associated with *SCHED*(*this)[.](#task.promise-6.sentence-1) [🔗](#lib:final_suspend,task::promise_type) `auto final_suspend() noexcept; ` [7](#task.promise-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7536) *Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4 Await")) whose member functions arrange for the completion of the asynchronous operation associated with *STATE*(*this) by invoking: - [(7.1)](#task.promise-7.1) set_error(std​::​move(*RCVR*(*this)), std​::​move(e)) if *errors*.index() is greater than zero ande is the value held by *errors*, otherwise - [(7.2)](#task.promise-7.2) set_value(std​::​move(*RCVR*(*this))) if is_void is true, and otherwise - [(7.3)](#task.promise-7.3) set_value(std​::​move(*RCVR*(*this)), **result*)[.](#task.promise-7.sentence-1) [🔗](#lib:yield_value,task::promise_type) `template auto yield_value(with_error err); ` [8](#task.promise-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7560) *Mandates*: std​::​move(err.error) is convertible to exactly one of theset_error_t argument types of error_types[.](#task.promise-8.sentence-1) Let *Cerr* be that type[.](#task.promise-8.sentence-2) [9](#task.promise-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7566) *Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4 Await")) whose member functions arrange for the calling coroutine to be suspended and then completes the asynchronous operation associated with*STATE*(*this) by invoking set_error(std​::​move(*RCVR*(*this)),*Cerr*(std​::​move(err.error)))[.](#task.promise-9.sentence-1) [🔗](#lib:await_transform,task::promise_type) `template<[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender> auto await_transform(Sender&& sndr) noexcept; ` [10](#task.promise-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7581) *Returns*: If [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]") is true returns as_awaitable(​std​::​​forward(sndr), *this); otherwise returnsas_awaitable(affine_on(​std​::​​forward(sndr), *SCHED*(*this)), *this)[.](#task.promise-10.sentence-1) [🔗](#lib:await_transform,task::promise_type_) `template auto await_transform(change_coroutine_scheduler sch) noexcept; ` [11](#task.promise-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7595) *Effects*: Equivalent to:return await_transform(just(exchange(*SCHED*(*this), scheduler_type(sch.scheduler))), *this); [🔗](#lib:uncaught_exception,task::promise_type) `void uncaught_exception(); ` [12](#task.promise-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7608) *Effects*: If the signature set_error_t(exception_ptr) is not an element of error_types, calls terminate() ([[except.terminate]](except.terminate "14.6.2 The std​::​terminate function"))[.](#task.promise-12.sentence-1) Otherwise, stores current_exception() into *errors*[.](#task.promise-12.sentence-2) [🔗](#lib:unhandled_stopped,task::promise_type) `coroutine_handle<> unhandled_stopped(); ` [13](#task.promise-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7620) *Effects*: Completes the asynchronous operation associated with *STATE*(*this) by invoking set_stopped(std​::​move(*RCVR*(*this)))[.](#task.promise-13.sentence-1) [14](#task.promise-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7626) *Returns*: noop_coroutine()[.](#task.promise-14.sentence-1) [🔗](#lib:get_env,task::promise_type) `unspecified get_env() const noexcept; ` [15](#task.promise-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7636) *Returns*: An object env such that queries are forwarded as follows: - [(15.1)](#task.promise-15.1) env.query(get_scheduler) returns scheduler_type(*SCHED*(*this))[.](#task.promise-15.1.sentence-1) - [(15.2)](#task.promise-15.2) env.query(get_allocator) returns *alloc*[.](#task.promise-15.2.sentence-1) - [(15.3)](#task.promise-15.3) env.query(get_stop_token) returns *token*[.](#task.promise-15.3.sentence-1) - [(15.4)](#task.promise-15.4) For any other query q and arguments a... a call to env.query(q, a...) returns*STATE*(*this)[.](#task.promise-15.4.sentence-1) environment.query(q, a...) if this expression is well-formed and forwarding_query(q) is well-formed and is true[.](#task.promise-15.4.sentence-2) Otherwise env.query(q, a...) is ill-formed[.](#task.promise-15.4.sentence-3) [🔗](#lib:operator_new,task::promise_type) `template void* operator new(size_t size, const Args&... args); ` [16](#task.promise-16) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7657) If there is no parameter with type allocator_arg_t then letalloc be allocator_type()[.](#task.promise-16.sentence-1) Otherwise, let arg_next be the parameter following the first allocator_arg_t parameter, and let alloc be allocator_type(arg_next)[.](#task.promise-16.sentence-2) Let PAlloc be allocator_traits​::​template rebind_alloc, where U is an unspecified type whose size and alignment are both __STDCPP_DEFAULT_NEW_ALIGNMENT__[.](#task.promise-16.sentence-3) [17](#task.promise-17) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7667) *Mandates*: - [(17.1)](#task.promise-17.1) The first parameter of type allocator_arg_t (if any) is not the last parameter[.](#task.promise-17.1.sentence-1) - [(17.2)](#task.promise-17.2) allocator_type(arg_next) is a valid expression if there is a parameter of type allocator_arg_t[.](#task.promise-17.2.sentence-1) - [(17.3)](#task.promise-17.3) allocator_traits​::​pointer is a pointer type[.](#task.promise-17.3.sentence-1) [18](#task.promise-18) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7676) *Effects*: Initializes an allocator palloc of type PAlloc withalloc[.](#task.promise-18.sentence-1) 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[.](#task.promise-18.sentence-2) [19](#task.promise-19) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7686) *Returns*: A pointer to the allocated storage[.](#task.promise-19.sentence-1) [🔗](#lib:operator_delete,task::promise_type) `void operator delete(void* pointer, size_t size) noexcept; ` [20](#task.promise-20) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7696) *Preconditions*: pointer was returned from an invocation of the above overload of operator new with a size argument equal to size[.](#task.promise-20.sentence-1) [21](#task.promise-21) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7701) *Effects*: Deallocates the storage pointed to by pointer using an allocator equal to that used to allocate it[.](#task.promise-21.sentence-1)