namespace std { namespace experimental { namespace net { inline namespace v1 { template<class CompletionToken, class Signature> class async_result; template<class CompletionToken, class Signature> struct async_completion; template<class T, class ProtoAllocator = allocator<void>> struct associated_allocator; template<class T, class ProtoAllocator = allocator<void>> using associated_allocator_t = typename associated_allocator<T, ProtoAllocator>::type; // [async.assoc.alloc.get], get_associated_allocator: template<class T> associated_allocator_t<T> get_associated_allocator(const T& t) noexcept; template<class T, class ProtoAllocator> associated_allocator_t<T, ProtoAllocator> get_associated_allocator(const T& t, const ProtoAllocator& a) noexcept; enum class fork_event { prepare, parent, child }; class execution_context; class service_already_exists; template<class Service> Service& use_service(execution_context& ctx); template<class Service, class... Args> Service& make_service(execution_context& ctx, Args&&... args); template<class Service> bool has_service(execution_context& ctx) noexcept; template<class T> struct is_executor; template<class T> constexpr bool is_executor_v = is_executor<T>::value; struct executor_arg_t { }; constexpr executor_arg_t executor_arg = executor_arg_t(); template<class T, class Executor> struct uses_executor; template<class T, class Executor> constexpr bool uses_executor_v = uses_executor<T, Executor>::value; template<class T, class Executor = system_executor> struct associated_executor; template<class T, class Executor = system_executor> using associated_executor_t = typename associated_executor<T, Executor>::type; // [async.assoc.exec.get], get_associated_executor: template<class T> associated_executor_t<T> get_associated_executor(const T& t) noexcept; template<class T, class Executor> associated_executor_t<T, Executor> get_associated_executor(const T& t, const Executor& ex) noexcept; template<class T, class ExecutionContext> associated_executor_t<T, typename ExecutionContext::executor_type> get_associated_executor(const T& t, ExecutionContext& ctx) noexcept; template<class T, class Executor> class executor_binder; template<class T, class Executor, class Signature> class async_result<executor_binder<T, Executor>, Signature>; template<class T, class Executor, class ProtoAllocator> struct associated_allocator<executor_binder<T, Executor>, ProtoAllocator>; template<class T, class Executor, class Executor1> struct associated_executor<executor_binder<T, Executor>, Executor1>; // [async.bind.executor], bind_executor: template<class Executor, class T> executor_binder<decay_t<T>, Executor> bind_executor(const Executor& ex, T&& t); template<class ExecutionContext, class T> executor_binder<decay_t<T>, typename ExecutionContext::executor_type> bind_executor(ExecutionContext& ctx, T&& t); template<class Executor> class executor_work_guard; // [async.make.work.guard], make_work_guard: template<class Executor> executor_work_guard<Executor> make_work_guard(const Executor& ex); template<class ExecutionContext> executor_work_guard<typename ExecutionContext::executor_type> make_work_guard(ExecutionContext& ctx); template<class T> executor_work_guard<associated_executor_t<T>> make_work_guard(const T& t); template<class T, class U> auto make_work_guard(const T& t, U&& u) -> decltype(make_work_guard(get_associated_executor(t, forward<U>(u)))); class system_executor; class system_context; bool operator==(const system_executor&, const system_executor&); bool operator!=(const system_executor&, const system_executor&); class bad_executor; class executor; bool operator==(const executor& a, const executor& b) noexcept; bool operator==(const executor& e, nullptr_t) noexcept; bool operator==(nullptr_t, const executor& e) noexcept; bool operator!=(const executor& a, const executor& b) noexcept; bool operator!=(const executor& e, nullptr_t) noexcept; bool operator!=(nullptr_t, const executor& e) noexcept; // [async.dispatch], dispatch: template<class CompletionToken> DEDUCED dispatch(CompletionToken&& token); template<class Executor, class CompletionToken> DEDUCED dispatch(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED dispatch(ExecutionContext& ctx, CompletionToken&& token); // [async.post], post: template<class CompletionToken> DEDUCED post(CompletionToken&& token); template<class Executor, class CompletionToken> DEDUCED post(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED post(ExecutionContext& ctx, CompletionToken&& token); // [async.defer], defer: template<class CompletionToken> DEDUCED defer(CompletionToken&& token); template<class Executor, class CompletionToken> DEDUCED defer(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED defer(ExecutionContext& ctx, CompletionToken&& token); template<class Executor> class strand; template<class Executor> bool operator==(const strand<Executor>& a, const strand<Executor>& b); template<class Executor> bool operator!=(const strand<Executor>& a, const strand<Executor>& b); template<class ProtoAllocator = allocator<void>> class use_future_t; constexpr use_future_t<> use_future = use_future_t<>(); template<class ProtoAllocator, class Result, class... Args> class async_result<use_future_t<ProtoAllocator>, Result(Args...)>; template<class Result, class... Args, class Signature> class async_result<packaged_task<Result(Args...)>, Signature>; } // inline namespace v1 } // namespace net } // namespace experimental template<class Allocator> struct uses_allocator<experimental::net::v1::executor, Allocator> : true_type {}; } // namespace std
A type A meets the proto-allocator requirements if A is CopyConstructible (C++ 2014 [copyconstructible]), Destructible (C++ 2014 [destructible]), and allocator_traits<A>::rebind_alloc<U> meets the allocator requirements (C++ 2014 [allocator.requirements]), where U is an object type. [ Note: For example, allocator<void> meets the proto-allocator requirements but not the allocator requirements. — end note ] No comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception.
The library describes a standard set of requirements for executors. A type meeting the Executor requirements embodies a set of rules for determining how submitted function objects are to be executed.
A type X meets the Executor requirements if it satisfies the requirements of CopyConstructible (C++ 2014 [copyconstructible]) and Destructible (C++ 2014 [destructible]), as well as the additional requirements listed below.
No comparison operator, copy operation, move operation, swap operation, or member functions context, on_work_started, and on_work_finished on these types shall exit via an exception.
The executor copy constructor, comparison operators, and other member functions defined in these requirements shall not introduce data races as a result of concurrent calls to those functions from different threads. The member function dispatch may be recursively reentered.
Let ctx be the execution context returned by the executor's context() member function. An executor becomes invalid when the first call to ctx.shutdown() returns. The effect of calling on_work_started, on_work_finished, dispatch, post, or defer on an invalid executor is undefined. [ Note: The copy constructor, comparison operators, and context() member function continue to remain valid until ctx is destroyed. — end note ]
In Table [tab:async.reqmts.executor.requirements], x1 and x2 denote (possibly const) values of type X, mx1 denotes an xvalue of type X, f denotes a function object of MoveConstructible (C++ 2014 [moveconstructible]) type Func such that f() is a valid expression, a denotes a (possibly const) value of type A where A is a type meeting the ProtoAllocator requirements ([async.reqmts.proto.allocator]), and u denotes an identifier.
expression | type | assertion/note pre/post-conditions |
X u(x1); |
Shall not exit via an exception. post: u == x1 and std::addressof(u.context()) == std::addressof(x1.context()). | |
X u(mx1); |
Shall not exit via an exception. post: u equals the prior value of mx1 and std::addressof(u.context()) equals the prior value of std::addressof(mx1.context()). | |
x1 == x2 | bool |
Returns true only if x1 and x2 can be interchanged with identical effects in any of the expressions defined in these type requirements. [ Note: Returning false does not necessarily imply that the effects are not identical. — end note ] operator== shall be reflexive, symmetric, and transitive, and shall not exit via an exception. |
x1 != x2 | bool | Same as !(x1 == x2). |
x1.context() | execution_context&, or E& where E is a type that satisfies the ExecutionContext ([async.reqmts.executioncontext]) requirements. | Shall not exit via an exception. The comparison operators and member functions defined in these requirements shall not alter the reference returned by this function. |
x1.on_work_started() | Shall not exit via an exception. | |
x1.on_work_finished() |
Shall not exit via an exception. Precondition: A preceding call x2.on_work_started() where x1 == x2. | |
x1.dispatch( std::move(f), a) |
Effects: Creates an object f1 initialized with DECAY_COPY(std::forward<Func>(f)) (C++ 2014 [thread.decaycopy]) in the current thread of execution. Calls f1() at most once. The executor may block forward progress of the caller until f1() finishes execution. Executor implementations should use the supplied allocator to allocate any memory required to store the function object. Prior to invoking the function object, the executor shall deallocate any memory allocated. [ Note: Executors defined in this document always use the supplied allocator unless otherwise specified. — end note ] Synchronization: The invocation of dispatch synchronizes with (C++ 2014 [intro.multithread]) the invocation of f1. | |
x1.post(std::move(f), a) |
Effects: Creates an object f1 initialized with DECAY_COPY(std::forward<Func>(f)) in the current thread of execution. Calls f1() at most once. The executor shall not block forward progress of the caller pending completion of f1(). The executor may begin f1's progress before the call to post completes. Executor implementations should use the supplied allocator to allocate any memory required to store the function object. Prior to invoking the function object, the executor shall deallocate any memory allocated. [ Note: Executors defined in this document always use the supplied allocator unless otherwise specified. — end note ] Synchronization: The invocation of post synchronizes with (C++ 2014 [intro.multithread]) the invocation of f1. | |
x1.defer(std::move(f), a) |
Effects: Creates an object f1 initialized with DECAY_COPY(std::forward<Func>(f)) in the current thread of execution. Calls f1() at most once. The executor shall not block forward progress of the caller pending completion of f1(). The executor should not begin f1's progress before the call to defer completes. [ Note: One use of defer is to convey the intention of the caller that f1 is a continuation of the current call context. The executor can use this information to optimize or otherwise adjust the way in which f1 is invoked. — end note ] Executor implementations should use the supplied allocator to allocate any memory required to store the function object. Prior to invoking the function object, the executor shall deallocate any memory allocated. [ Note: Executors defined in this document always use the supplied allocator unless otherwise specified. — end note ] Synchronization: The invocation of defer synchronizes with (C++ 2014 [intro.multithread]) the invocation of f1. |
A type X meets the ExecutionContext requirements if it is publicly and unambiguously derived from execution_context, and satisfies the additional requirements listed below.
In Table [tab:async.reqmts.executioncontext.requirements], x denotes a value of type X.
expression | return type | assertion/note pre/post-condition |
X::executor_type | type meeting Executor ([async.reqmts.executor]) requirements | |
x.~X() | Destroys all unexecuted function objects that were submitted via an executor object that is associated with the execution context. | |
x.get_executor() | X::executor_type | Returns an executor object that is associated with the execution context. |
A class is a service if it is publicly and unambiguously derived from execution_context::service, or if it is publicly and unambiguously derived from another service. For a service S, S::key_type shall be valid and denote a type (C++ 2014 [temp.deduct]), is_base_of_v<typename S::key_type, S> shall be true, and S shall satisfy the Destructible requirements (C++ 2014 [destructible]).
The first parameter of all service constructors shall be an lvalue reference to execution_context. This parameter denotes the execution_context object that represents a set of services, of which the service object will be a member. [ Note: These constructors can be called by the make_service function. — end note ]
A service shall provide an explicit constructor with a single parameter of lvalue reference to execution_context. [ Note: This constructor can be called by the use_service function. — end note ]
[ Example:
class my_service : public execution_context::service{ public: using key_type = my_service; explicit my_service(execution_context& ctx); my_service(execution_context& ctx, int some_value); private: virtual void shutdown() noexcept override; ... };
— end example ]
A service's shutdown member function shall destroy all copies of function objects that are held by the service.
A type satisfies the signature requirements if it is a call signature (C++ 2014 [func.def]).
An associator defines a relationship between different types and objects where, given:
a source object s of type S,
type requirements R, and
a candidate object c of type C meeting the type requirements R,
an associated type A meeting the type requirements R may be computed, and an associated object a of type A may be obtained.
An associator shall be a class template that takes two template type arguments. The first template argument is the source type S. The second template argument is the candidate type C. The second template argument shall be defaulted to some default candidate type D that satisfies the type requirements R.
An associator shall additionally satisfy the requirements in Table [tab:async.reqmts.associator.requirements]. In this table, X is a class template that meets the associator requirements, S is the source type, s is a value of type S or const S, C is the candidate type, c is a (possibly const) value of type C, D is the default candidate type, and d is a (possibly const) value of type D that is the default candidate object.
expression | return type | assertion/note pre/post-conditions |
X<S>::type | X<S, D>::type | |
X<S, C>::type | The associated type. | |
X<S>::get(s) | X<S>::type | Returns X<S>::get(S, d). |
X<S, C>::get(s, c) | X<S, C>::type | Returns the associated object. |
The associator's primary template shall be defined. A program may partially specialize the associator class template for some user-defined type S.
Finally, the associator shall provide the following type alias and function template in the enclosing namespace:
template<class S, class C = D> using X_t = typename X<S, C>::type; template<class S, class C = D> typename X<S, C>::type get_X(const S& s, const C& c = d){ return X<S, C>::get(s, c); }
where X is replaced with the name of the associator class template. [ Note: This function template is provided as a convenience, to automatically deduce the source and candidate types. — end note ]
This subclause uses the names Alloc1, Alloc2, alloc1, alloc2, Args, CompletionHandler, completion_handler, Executor1, Executor2, ex1, ex2, f, i, N, Signature, token, Ti, ti, work1, and work2 as placeholders for specifying the requirements below.
An initiating function is a function which may be called to start an asynchronous operation. A completion handler is a function object that will be invoked, at most once, with the result of the asynchronous operation.
The life cycle of an asynchronous operation is comprised of the following events and phases:
Event 1: The asynchronous operation is started by a call to the initiating function.
Phase 1: The asynchronous operation is now outstanding.
Event 2: The externally observable side effects of the asynchronous operation, if any, are fully established. The completion handler is submitted to an executor.
Phase 2: The asynchronous operation is now completed.
Event 3: The completion handler is called with the result of the asynchronous operation.
In this document, all functions with the prefix async_ are initiating functions.
Initiating functions:
are function templates with template parameter CompletionToken;
accept, as the final parameter, a completion token object token of type CompletionToken;
specify a completion signature, which is a call signature (C++ 2014 [func.def]) Signature that determines the arguments to the completion handler.
An initiating function determines the type CompletionHandler of its completion handler function object by performing typename async_result<decay_t<CompletionToken>, Signature>::completion_handler_type. The completion handler object completion_handler is initialized with std::forward<CompletionToken>(token). [ Note: No other requirements are placed on the type CompletionToken. — end note ]
The type CompletionHandler shall satisfy the requirements of Destructible (C++ 2014 [destructible]) and MoveConstructible (C++ 2014 [moveconstructible]), and be callable with the specified call signature.
In this document, all initiating functions specify a Completion signature: element that defines the call signature Signature. The Completion signature: elements in this document have named parameters, and the results of an asynchronous operation are specified in terms of these names.
The return type of an initiating function is typename async_result<decay_t<CompletionToken>, Signature>::return_type.
For the sake of exposition, this document sometimes annotates functions with a return type DEDUCED. For every function declaration that returns DEDUCED, the meaning is equivalent to specifying the return type as typename async_result<decay_t<CompletionToken>, Signature>::return_type.
An initiating function produces its return type as follows:
constructing an object result of type async_result<decay_t<CompletionToken>, Signature>, initialized as result(completion_handler); and
using result.get() as the operand of the return statement.
[ Example: Given an asynchronous operation with Completion signature void(R1 r1, R2 r2), an initiating function meeting these requirements may be implemented as follows:
template<class CompletionToken> auto async_xyz(T1 t1, T2 t2, CompletionToken&& token){ typename async_result<decay_t<CompletionToken>, void(R1, R2)>::completion_handler_type completion_handler(forward<CompletionToken>(token)); async_result<decay_t<CompletionToken>, void(R1, R2)> result(completion_handler); // initiate the operation and cause completion_handler to be invoked with // the result return result.get(); }
For convenience, initiating functions may be implemented using the async_completion template:
template<class CompletionToken> auto async_xyz(T1 t1, T2 t2, CompletionToken&& token){ async_completion<CompletionToken, void(R1, R2)> init(token); // initiate the operation and cause init.completion_handler to be invoked // with the result return init.result.get(); }
— end example ]
Unless otherwise specified, the lifetime of arguments to initiating functions shall be treated as follows:
If the parameter has a pointer type or has a type of lvalue reference to non-const, the implementation may assume the validity of the pointee or referent, respectively, until the completion handler is invoked. [ Note: In other words, the program is responsible for guaranteeing the validity of the argument until the completion handler is invoked. — end note ]
Otherwise, the implementation does not assume the validity of the argument after the initiating function completes. [ Note: In other words, the program is not required to guarantee the validity of the argument after the initiating function completes. — end note ] The implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the completion handler.
An initiating function shall not block (C++ 2014 [defns.block]) the calling thread pending completion of the outstanding operation.
[ Note: Initiating functions can still block the calling thread for other reasons. For example, if an initiating function locks a mutex in order to synchronize access to shared data. — end note ]
Certain objects that participate in asynchronous operations have an associated executor. These are obtained as specified below.
An asynchronous operation has an associated executor satisfying the Executor ([async.reqmts.executor]) requirements. If not otherwise specified by the asynchronous operation, this associated executor is an object of type system_executor.
All asynchronous operations in this document have an associated executor object that is determined as follows:
If the initiating function is a member function, the associated executor is that returned by the get_executor member function on the same object.
If the initiating function is not a member function, the associated executor is that returned by the get_executor member function of the first argument to the initiating function.
Let Executor1 be the type of the associated executor. Let ex1 be a value of type Executor1, representing the associated executor object obtained as described above.
A completion handler object of type CompletionHandler has an associated executor satisfying the Executor requirements ([async.reqmts.executor]). The type of this associated executor is associated_executor_t<CompletionHandler, Executor1>. Let Executor2 be the type associated_executor_t<CompletionHandler, Executor1>. Let ex2 be a value of type Executor2 obtained by performing get_associated_executor(completion_handler, ex1).
Until the asynchronous operation has completed, the asynchronous operation shall maintain:
an object work1 of type executor_work_guard<Executor1>, initialized as work1(ex1), and where work1.owns_work() == true; and
an object work2 of type executor_work_guard<Executor2>, initialized as work2(ex2), and where work2.owns_work() == true.
Asynchronous operations may allocate memory. [ Note: Such as a data structure to store copies of the completion_handler object and the initiating function's arguments. — end note ]
Let Alloc1 be a type, satisfying the ProtoAllocator ([async.reqmts.proto.allocator]) requirements, that represents the asynchronous operation's default allocation strategy. [ Note: Typically allocator<void>. — end note ] Let alloc1 be a value of type Alloc1.
A completion handler object of type CompletionHandler has an associated allocator object alloc2 of type Alloc2 satisfying the ProtoAllocator ([async.reqmts.proto.allocator]) requirements. The type Alloc2 is associated_allocator_t<CompletionHandler, Alloc1>. Let alloc2 be a value of type Alloc2 obtained by performing get_associated_allocator(completion_handler, alloc1).
The asynchronous operations defined in this document:
If required, allocate memory using only the completion handler's associated allocator.
Prior to completion handler execution, deallocate any memory allocated using the completion handler's associated allocator.
[ Note: The implementation can perform operating system or underlying API calls that perform memory allocations not using the associated allocator. Invocations of the allocator functions do not introduce data races (see C++ 2014 [res.on.data.races]). — end note ]
Let Args... be the argument types of the completion signature Signature and let N be sizeof...(Args). Let i be in the range [0, N). Let Ti be the ith type in Args... and let ti be the ith completion handler argument associated with Ti.
Let f be a function object, callable as f(), that invokes completion_handler as if by completion_handler(forward<T0>(t0), ..., forward<TN-1>(tN-1)).
If an asynchronous operation completes immediately (that is, within the thread of execution calling the initiating function, and before the initiating function returns), the completion handler shall be submitted for execution as if by performing ex2.post(std::move(f), alloc2). Otherwise, the completion handler shall be submitted for execution as if by performing ex2.dispatch(std::move(f), alloc2).
Completion handlers are permitted to throw exceptions. The effect of any exception propagated from the execution of a completion handler is determined by the executor which is executing the completion handler.
In this document, a composed asynchronous operation is an asynchronous operation that is implemented in terms of zero or more intermediate calls to other asynchronous operations. The intermediate asynchronous operations are performed sequentially. [ Note: That is, the completion handler of an intermediate operation initiates the next operation in the sequence. — end note ]
An intermediate operation's completion handler shall have an associated executor that is either:
the type Executor2 and object ex2 obtained from the completion handler type CompletionHandler and object completion_handler; or
an object of an unspecified type satisfying the Executor requirements ([async.reqmts.executor]), that delegates executor operations to the type Executor2 and object ex2.
An intermediate operation's completion handler shall have an associated allocator that is either:
the type Alloc2 and object alloc2 obtained from the completion handler type CompletionHandler and object completion_handler; or
an object of an unspecified type satisfying the ProtoAllocator requirements ([async.reqmts.proto.allocator]), that delegates allocator operations to the type Alloc2 and object alloc2.
The async_result class template is a customization point for asynchronous operations. Template parameter CompletionToken specifies the model used to obtain the result of the asynchronous operation. Template parameter Signature is the call signature (C++ 2014 [func.def]) for the completion handler type invoked on completion of the asynchronous operation. The async_result template:
transforms a CompletionToken into a completion handler type that is based on a Signature; and
determines the return type and return value of an asynchronous operation's initiating function.
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class CompletionToken, class Signature> class async_result { public: using completion_handler_type = CompletionToken; using return_type = void; explicit async_result(completion_handler_type&) {} async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; return_type get() {} }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
The template parameter CompletionToken shall be an object type. The template parameter Signature shall be a call signature (C++ 2014 [func.def]).
Specializations of async_result shall satisfy the Destructible requirements (C++ 2014 [destructible]) in addition to the requirements in Table [tab:async.async.result.requirements]. In this table, R is a specialization of async_result; r is a modifiable lvalue of type R; and h is a modifiable lvalue of type R::completion_handler_type.
Expression | Return type | Requirement |
R::completion_handler_type | A type satisfying MoveConstructible requirements (C++ 2014 [moveconstructible]), An object of type completion_handler_type shall be a function object with call signature Signature, and completion_handler_type shall be constructible with an rvalue of type CompletionToken. | |
R::return_type | void; or a type satisfying MoveConstructible requirements (C++ 2014 [moveconstructible]) | |
R r(h); | ||
r.get() | R::return_type | [ Note: An asynchronous operation's initiating function uses the get() member function as the sole operand of a return statement. — end note ] |
Class template async_completion is provided as a convenience, to simplify the implementation of asynchronous operations that use async_result.
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class CompletionToken, class Signature> struct async_completion { using completion_handler_type = async_result<decay_t<CompletionToken>, Signature>::completion_handler_type; explicit async_completion(CompletionToken& t); async_completion(const async_completion&) = delete; async_completion& operator=(const async_completion&) = delete; see below completion_handler; async_result<decay_t<CompletionToken>, Signature> result; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
The template parameter Signature shall be a call signature (C++ 2014 [func.def]).
explicit async_completion(CompletionToken& t);
Effects: If CompletionToken and completion_handler_type are the same type, binds completion_handler to t; otherwise, initializes completion_handler with std::forward<CompletionToken>(t). Initializes result with completion_handler.
see below completion_handler;
Type: completion_handler_type& if CompletionToken and completion_handler_type are the same type; otherwise, completion_handler_type.
Class template associated_allocator is an associator ([async.reqmts.associator]) for the ProtoAllocator ([async.reqmts.proto.allocator]) type requirements, with default candidate type allocator<void> and default candidate object allocator<void>().
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class ProtoAllocator = allocator<void>> struct associated_allocator { using type = see below; static type get(const T& t, const ProtoAllocator& a = ProtoAllocator()) noexcept; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Specializations of associated_allocator shall satisfy the requirements in Table [tab:async.assoc.alloc.requirements]. In this table, X is a specialization of associated_allocator for the template parameters T and ProtoAllocator; t is a (possibly const) value of type T; and a is an object of type ProtoAllocator.
Expression | Return type | Note |
typename X::type | A type meeting the proto-allocator ([async.reqmts.proto.allocator]) requirements. | |
X::get(t) | X::type | Shall not exit via an exception. Equivalent to X::get(t, ProtoAllocator()). |
X::get(t, a) | X::type | Shall not exit via an exception. |
using type = see below;
Type: T::allocator_type if the qualified-id T::allocator_type is valid and denotes a type (C++ 2014 [temp.deduct]). Otherwise ProtoAllocator.
type get(const T& t, const ProtoAllocator& a = ProtoAllocator()) noexcept;
Returns: t.get_allocator() if the qualified-id T::allocator_type is valid and denotes a type (C++ 2014 [temp.deduct]). Otherwise a.
template<class T>
associated_allocator_t<T> get_associated_allocator(const T& t) noexcept;
Returns: associated_allocator<T>::get(t).
template<class T, class ProtoAllocator>
associated_allocator_t<T, ProtoAllocator>
get_associated_allocator(const T& t, const ProtoAllocator& a) noexcept;
Returns: associated_allocator<T, ProtoAllocator>::get(t, a).
Class execution_context implements an extensible, type-safe, polymorphic set of services, indexed by service type.
namespace std { namespace experimental { namespace net { inline namespace v1 { class execution_context { public: class service; // [async.exec.ctx.cons], construct / copy / destroy: execution_context(); execution_context(const execution_context&) = delete; execution_context& operator=(const execution_context&) = delete; virtual ~execution_context(); // [async.exec.ctx.ops], execution context operations: void notify_fork(fork_event e); protected: // [async.exec.ctx.protected], execution context protected operations: void shutdown() noexcept; void destroy() noexcept; }; // service access: template<class Service> typename Service::key_type& use_service(execution_context& ctx); template<class Service, class... Args> Service& make_service(execution_context& ctx, Args&&... args); template<class Service> bool has_service(const execution_context& ctx) noexcept; class service_already_exists : public logic_error { }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Access to the services of an execution_context is via three function templates, use_service, make_service and has_service.
In a call to use_service<Service>, the type argument chooses a service, identified by Service::key_type, from a set of services in an execution_context. If the service is not present in the execution_context, an object of type Service is created and added to the execution_context. A program can check if an execution_context implements a particular service with the function template has_service<Service>.
Service objects may be explicitly added to an execution_context using the function template make_service<Service>. If the service is already present, make_service exits via an exception of type service_already_exists.
Once a service reference is obtained from an execution_context object by calling use_service, that reference remains usable until a call to destroy().
If a call to a specialization of use_service or make_service recursively calls another specialization of use_service or make_service which would choose the same service (identified by key_type) from the same execution_context, then the behavior is undefined. [ Note: Nested calls to specializations for different service types are well-defined. — end note ]
Effects: Creates an object of class execution_context which contains no services. [ Note: An implementation can preload services of internal service types for its own use. — end note ]
Effects: Destroys an object of class execution_context. Performs shutdown() followed by destroy().
Effects: For each service object svc in the execution_context set, in reverse order of addition to the set, performs svc->shutdown(). For each service in the set, svc->shutdown() is called only once irrespective of the number of calls to shutdown on the execution_context.
void destroy() noexcept;
Effects: Destroys each service object in the execution_context set, and removes it from the set, in reverse order of addition to the set.
The functions use_service, make_service, and has_service do not introduce data races as a result of concurrent calls to those functions from different threads.
template<class Service> typename Service::key_type&
use_service(execution_context& ctx);
Effects: If an object of type Service::key_type does not already exist in the execution_context set identified by ctx, creates an object of type Service, initialized as Service(ctx), and adds it to the set.
Returns: A reference to the corresponding service of ctx.
Remarks: The reference returned remains valid until a call to destroy.
template<class Service, class... Args> Service&
make_service(execution_context& ctx, Args&&... args);
Requires: A service object of type Service::key_type does not already exist in the execution_context set identified by ctx.
Effects: Creates an object of type Service, initialized as Service(ctx, forward<Args>(args)...), and adds it to the execution_context set identified by ctx.
Throws: service_already_exists if a corresponding service object of type Service::key_type is already present in the set.
Remarks: The reference returned remains valid until a call to destroy.
template<class Service> bool has_service(const execution_context& ctx) noexcept;
Returns: true if an object of type Service::key_type is present in ctx, otherwise false.
namespace std { namespace experimental { namespace net { inline namespace v1 { class execution_context::service { protected: // construct / copy / destroy: explicit service(execution_context& owner); service(const service&) = delete; service& operator=(const service&) = delete; virtual ~service(); // service observers: execution_context& context() noexcept; private: // service operations: virtual void shutdown() noexcept = 0; virtual void notify_fork(fork_event e) {} execution_context& context_; // exposition only }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
explicit service(execution_context& owner);
Postconditions: std::addressof(context_) == std::addressof(owner).
execution_context& context() noexcept;
Returns: context_.
The class template is_executor can be used to detect executor types satisfying the Executor ([async.reqmts.executor]) type requirements.
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T> struct is_executor; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
T shall be a complete type.
Class template is_executor is a UnaryTypeTrait (C++ 2014 [meta.rqmts]) with a BaseCharacteristic of true_type if the type T meets the syntactic requirements for Executor ([async.reqmts.executor]), otherwise false_type.
namespace std { namespace experimental { namespace net { inline namespace v1 { struct executor_arg_t { }; constexpr executor_arg_t executor_arg = executor_arg_t(); } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
The executor_arg_t struct is an empty structure type used as a unique type to disambiguate constructor and function overloading. Specifically, types may have constructors with executor_arg_t as the first argument, immediately followed by an argument of a type that satisfies the Executor requirements ([async.reqmts.executor]).
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class Executor> struct uses_executor; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Remark: Detects whether T has a nested executor_type that is convertible from Executor. Meets the BinaryTypeTrait requirements (C++ 2014 [meta.rqmts]). The implementation provides a definition that is derived from true_type if the qualified-id T::executor_type is valid and denotes a type and is_convertible<Executor, T::executor_type>::value != false, otherwise it is derived from false_type. A program may specialize this template to derive from true_type for a user-defined type T that does not have a nested executor_type but nonetheless can be constructed with an executor if the first argument of a constructor has type executor_arg_t and the second argument has type Executor.
Uses-executor construction with executor Executor refers to the construction of an object obj of type T, using constructor arguments v1, v2, ..., vN of types V1, V2, ..., VN, respectively, and an executor ex of type Executor, according to the following rules:
if uses_executor_v<T, Executor> is true and is_constructible<T, executor_arg_t, Executor, V1, V2, ..., VN>::value is true, then obj is initialized as obj(executor_arg, ex, v1, v2, ..., vN);
otherwise, obj is initialized as obj(v1, v2, ..., vN).
Class template associated_executor is an associator ([async.reqmts.associator]) for the Executor ([async.reqmts.executor]) type requirements, with default candidate type system_executor and default candidate object system_executor().
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class Executor = system_executor> struct associated_executor { using type = see below; static type get(const T& t, const Executor& e = Executor()) noexcept; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Specializations of associated_executor shall satisfy the requirements in Table [tab:async.assoc.exec.requirements]. In this table, X is a specialization of associated_executor for the template parameters T and Executor; t is a (possible const) value of T; and e is an object of type Executor.
Expression | Return type | Note |
typename X::type | A type meeting Executor requirements ([async.reqmts.executor]). | |
X::get(t) | X::type | Shall not exit via an exception. Equivalent to X::get(t, Executor()). |
X::get(t, e) | X::type | Shall not exit via an exception. |
using type = see below;
Type: T::executor_type if the qualified-id T::executor_type is valid and denotes a type (C++ 2014 [temp.deduct]). Otherwise Executor.
type get(const T& t, const Executor& e = Executor()) noexcept;
Returns: t.get_executor() if the qualified-id T::executor_type is valid and denotes a type (C++ 2014 [temp.deduct]). Otherwise e.
template<class T>
associated_executor_t<T> get_associated_executor(const T& t) noexcept;
Returns: associated_executor<T>::get(t).
template<class T, class Executor>
associated_executor_t<T, Executor>
get_associated_executor(const T& t, const Executor& ex) noexcept;
Returns: associated_executor<T, Executor>::get(t, ex).
Remarks: This function shall not participate in overload resolution unless is_executor_v<Executor> is true.
template<class T, class ExecutionContext>
associated_executor_t<T, typename ExecutionContext::executor_type>
get_associated_executor(const T& t, ExecutionContext& ctx) noexcept;
Returns: get_associated_executor(t, ctx.get_executor()).
Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.
The class template executor_binder binds executors to objects. A specialization executor_binder<T, Executor> binds an executor of type Executor satisfying the Executor requirements ([async.reqmts.executor]) to an object or function of type T.
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class Executor> class executor_binder { public: // types: using target_type = T; using executor_type = Executor; // [async.exec.binder.cons], construct / copy / destroy: executor_binder(T t, const Executor& ex); executor_binder(const executor_binder& other) = default; executor_binder(executor_binder&& other) = default; template<class U, class OtherExecutor> executor_binder(const executor_binder<U, OtherExecutor>& other); template<class U, class OtherExecutor> executor_binder(executor_binder<U, OtherExecutor>&& other); template<class U, class OtherExecutor> executor_binder(executor_arg_t, const Executor& ex, const executor_binder<U, OtherExecutor>& other); template<class U, class OtherExecutor> executor_binder(executor_arg_t, const Executor& ex, executor_binder<U, OtherExecutor>&& other); ~executor_binder(); // [async.exec.binder.access], executor binder access: T& get() noexcept; const T& get() const noexcept; executor_type get_executor() const noexcept; // [async.exec.binder.invocation], executor binder invocation: template<class... Args> result_of_t<T&(Args&&...)> operator()(Args&&... args); template<class... Args> result_of_t<const T&(Args&&...)> operator()(Args&&... args) const; private: Executor ex_; // exposition only T target_; // exposition only }; template<class T, class Executor, class Signature> class async_result<executor_binder<T, Executor>, Signature>; template<class T, class Executor, class ProtoAllocator> struct associated_allocator<executor_binder<T, Executor>, ProtoAllocator>; template<class T, class Executor, class Executor1> struct associated_executor<executor_binder<T, Executor>, Executor1>; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
executor_binder(T t, const Executor& ex);
Effects: Initializes ex_ with ex. Initializes target_ by performing uses-executor construction, using the constructor argument std::move(t) and the executor ex_.
template<class U, class OtherExecutor>
executor_binder(const executor_binder<U, OtherExecutor>& other);
Requires: If U is not convertible to T, or if OtherExecutor is not convertible to Executor, the program is ill-formed.
Effects: Initializes ex_ with other.get_executor(). Initializes target_ by performing uses-executor construction, using the constructor argument other.get() and the executor ex_.
template<class U, class OtherExecutor>
executor_binder(executor_binder<U, OtherExecutor>&& other);
Requires: If U is not convertible to T, or if OtherExecutor is not convertible to Executor, the program is ill-formed.
Effects: Initializes ex_ with other.get_executor(). Initializes target_ by performing uses-executor construction, using the constructor argument std::move(other.get()) and the executor ex_.
template<class U, class OtherExecutor>
executor_binder(executor_arg_t, const Executor& ex,
const executor_binder<U, OtherExecutor>& other);
Requires: If U is not convertible to T the program is ill-formed.
Effects: Initializes ex_ with ex. Initializes target_ by performing uses-executor construction, using the constructor argument other.get() and the executor ex_.
template<class U, class OtherExecutor>
executor_binder(executor_arg_t, const Executor& ex,
executor_binder<U, OtherExecutor>&& other);
Requires: U is T or convertible to T.
Effects: Initializes ex_ with ex. Initializes target_ by performing uses-executor construction, using the constructor argument std::move(other.get()) and the executor ex_.
T& get() noexcept;
const T& get() const noexcept;
Returns: target_.
executor_type get_executor() const noexcept;
Returns: executor_.
template<class... Args>
result_of_t<T&(Args&&...)> operator()(Args&&... args);
template<class... Args>
result_of_t<const T&(Args&&...)> operator()(Args&&... args) const;
Returns: INVOKE(get(), forward<Args>(args)...) (C++ 2014 [func.require]).
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class Executor, class Signature> class async_result<executor_binder<T, Executor>, Signature> { public: using completion_handler_type = executor_binder< typename async_result<T, Signature>::completion_handler_type, Executor>; using return_type = typename async_result<T, Signature>::return_type; explicit async_result(completion_handler_type& h); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; return_type get(); private: async_result<T, Signature> target_; // exposition only }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
explicit async_result(completion_handler_type& h);
Effects: Initializes target_ as target_(h.get()).
return_type get();
Returns: target_.get().
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class Executor, class ProtoAllocator> struct associated_allocator<executor_binder<T, Executor>, ProtoAllocator> { using type = associated_allocator_t<T, ProtoAllocator>; static type get(const executor_binder<T, Executor>& b, const ProtoAllocator& a = ProtoAllocator()) noexcept; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
static type get(const executor_binder<T, Executor>& b,
const ProtoAllocator& a = ProtoAllocator()) noexcept;
Returns: associated_allocator<T, ProtoAllocator>::get(b.get(), a).
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class T, class Executor, class Executor1> struct associated_executor<executor_binder<T, Executor>, Executor1> { using type = Executor; static type get(const executor_binder<T, Executor>& b, const Executor1& e = Executor1()) noexcept; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
static type get(const executor_binder<T, Executor>& b,
const Executor1& e = Executor1()) noexcept;
Returns: b.get_executor().
template<class Executor, class T>
executor_binder<decay_t<T>, Executor>
bind_executor(const Executor& ex, T&& t);
Returns: executor_binder<decay_t<T>, Executor>(forward<T>(t), ex).
Remarks: This function shall not participate in overload resolution unless is_executor_v<Executor> is true.
template<class ExecutionContext, class CompletionToken>
executor_binder<decay_t<T>, typename ExecutionContext::executor_type>
bind_executor(ExecutionContext& ctx, T&& t);
Returns: bind_executor(ctx.get_executor(), forward<T>(t)).
Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Executor> class executor_work_guard { public: // types: using executor_type = Executor; // construct / copy / destroy: explicit executor_work_guard(const executor_type& ex) noexcept; executor_work_guard(const executor_work_guard& other) noexcept; executor_work_guard(executor_work_guard&& other) noexcept; executor_work_guard& operator=(const executor_work_guard&) = delete; ~executor_work_guard(); // executor work guard observers: executor_type get_executor() const noexcept; bool owns_work() const noexcept; // executor work guard modifiers: void reset() noexcept; private: Executor ex_; // exposition only bool owns_; // exposition only }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
explicit executor_work_guard(const executor_type& ex) noexcept;
Effects: Initializes ex_ with ex, and then performs ex_.on_work_started().
Postconditions: ex_ == ex and owns_ == true.
executor_work_guard(const executor_work_guard& other) noexcept;
Effects: Initializes ex_ with other.ex_. If other.owns_ == true, performs ex_.on_work_started().
Postconditions: ex_ == other.ex_ and owns_ == other.owns_.
executor_work_guard(executor_work_guard&& other) noexcept;
Effects: Initializes ex_ with std::move(other.ex_) and initializes owns_ with other.owns_, and sets other.owns_ to false.
Effects: If owns_ is true, performs ex_.on_work_finished().
executor_type get_executor() const noexcept;
Returns: ex_.
bool owns_work() const noexcept;
Returns: owns_.
Effects: If owns_ is true, performs ex_.on_work_finished().
Postconditions: owns_ == false.
template<class Executor>
executor_work_guard<Executor>
make_work_guard(const Executor& ex);
Returns: executor_work_guard<Executor>(ex).
Remarks: This function shall not participate in overload resolution unless is_executor_v<Executor> is true.
template<class ExecutionContext>
executor_work_guard<typename ExecutionContext::executor_type>
make_work_guard(ExecutionContext& ctx);
Returns: make_work_guard(ctx.get_executor()).
Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.
template<class T>
executor_work_guard<associated_executor_t<T>>
make_work_guard(const T& t);
Returns: make_work_guard(get_associated_executor(t)).
Remarks: This function shall not participate in overload resolution unless is_executor_v<T> is false and is_convertible<T&, execution_context&>::value is false.
template<class T, class U>
auto make_work_guard(const T& t, U&& u)
-> decltype(make_work_guard(get_associated_executor(t, forward<U>(u))));
Returns: make_work_guard(get_associated_executor(t, forward<U>(u))).
Class system_executor represents a set of rules where function objects are permitted to execute on any thread.
namespace std { namespace experimental { namespace net { inline namespace v1 { class system_executor { public: // constructors: system_executor() {} // [async.system.exec.ops], executor operations: system_context& context() const noexcept; void on_work_started() const noexcept {} void on_work_finished() const noexcept {} template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a) const; template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a) const; template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a) const; }; bool operator==(const system_executor&, const system_executor&) noexcept; bool operator!=(const system_executor&, const system_executor&) noexcept; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
Class system_executor satisfies the Destructible (C++ 2014 [destructible]), DefaultConstructible (C++ 2014 [defaultconstructible]), and Executor ([async.reqmts.executor]) type requirements.
To satisfy the Executor requirements for the post and defer member functions, the system executor may create thread objects to run the submitted function objects. These thread objects are collectively referred to as system threads.
system_context& context() const noexcept;
Returns: A reference to an object with static storage duration. All calls to this function return references to the same object.
template<class Func, class ProtoAllocator>
void dispatch(Func&& f, const ProtoAllocator& a) const;
Effects: Equivalent to DECAY_COPY(forward<Func>(f))() (C++ 2014 [thread.decaycopy]).
template<class Func, class ProtoAllocator>
void post(Func&& f, const ProtoAllocator& a) const;
template<class Func, class ProtoAllocator>
void defer(Func&& f, const ProtoAllocator& a) const;
Effects: If context().stopped() is false, creates an object f1 initialized with DECAY_COPY(forward<Func>(f)), and calls f1() as if in a thread of execution represented by a thread object. Any exception propagated from the execution of DECAY_COPY(forward<Func>(f))() results in a call to std::terminate.
bool operator==(const system_executor&, const system_executor&) noexcept;
Returns: true.
bool operator!=(const system_executor&, const system_executor&) noexcept;
Returns: false.
Class system_context implements the execution context associated with system_executor objects.
namespace std { namespace experimental { namespace net { inline namespace v1 { class system_context : public execution_context { public: // types: using executor_type = system_executor; // construct / copy / destroy: system_context() = delete; system_context(const system_context&) = delete; system_context& operator=(const system_context&) = delete; ~system_context(); // system_context operations: executor_type get_executor() noexcept; void stop(); bool stopped() const noexcept; void join(); }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
The class system_context satisfies the ExecutionContext ([async.reqmts.executioncontext]) type requirements.
The system_context member functions get_executor, stop, and stopped, and the system_executor copy constructors, member functions and comparison operators, do not introduce data races as a result of concurrent calls to those functions from different threads of execution.
Effects: Performs stop() followed by join().
executor_type get_executor() noexcept;
Returns: system_executor().
Effects: Signals all system threads to exit as soon as possible. If a system thread is currently executing a function object, the thread will exit only after completion of that function object. Returns without waiting for the system threads to complete.
Postconditions: stopped() == true.
bool stopped() const noexcept;
Returns: true if the system_context has been stopped by a prior call to stop.
Effects: Blocks the calling thread (C++ 2014 [defns.block]) until all system threads have completed.
Synchronization: The completion of each system thread synchronizes with (C++ 2014 [intro.multithread]) the corresponding successful join() return.
An exception of type bad_executor is thrown by executor member functions dispatch, post, and defer when the executor object has no target.
namespace std { namespace experimental { namespace net { inline namespace v1 { class bad_executor : public exception { public: // constructor: bad_executor() noexcept; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
bad_executor() noexcept;
Effects: constructs a bad_executor object.
The executor class provides a polymorphic wrapper for types that satisfy the Executor requirements ([async.reqmts.executor]).
namespace std { namespace experimental { namespace net { inline namespace v1 { class executor { public: // [async.executor.cons], construct / copy / destroy: executor() noexcept; executor(nullptr_t) noexcept; executor(const executor& e) noexcept; executor(executor&& e) noexcept; template<class Executor> executor(Executor e); template<class Executor, class ProtoAllocator> executor(allocator_arg_t, const ProtoAllocator& a, Executor e); executor& operator=(const executor& e) noexcept; executor& operator=(executor&& e) noexcept; executor& operator=(nullptr_t) noexcept; template<class Executor> executor& operator=(Executor e); ~executor(); // [async.executor.modifiers], executor modifiers: void swap(executor& other) noexcept; template<class Executor, class ProtoAllocator> void assign(Executor e, const ProtoAllocator& a); // [async.executor.ops], executor operations: execution_context& context() const noexcept; void on_work_started() const noexcept; void on_work_finished() const noexcept; template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a) const; template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a) const; template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a) const; // [async.executor.capacity], executor capacity: explicit operator bool() const noexcept; // [async.executor.target], executor target access: const type_info& target_type() const noexcept; template<class Executor> Executor* target() noexcept; template<class Executor> const Executor* target() const noexcept; }; // [async.executor.comparisons], executor comparisons: bool operator==(const executor& a, const executor& b) noexcept; bool operator==(const executor& e, nullptr_t) noexcept; bool operator==(nullptr_t, const executor& e) noexcept; bool operator!=(const executor& a, const executor& b) noexcept; bool operator!=(const executor& e, nullptr_t) noexcept; bool operator!=(nullptr_t, const executor& e) noexcept; // [async.executor.algo], executor specialized algorithms: void swap(executor& a, executor& b) noexcept; } // inline namespace v1 } // namespace net } // namespace experimental template<class Allocator> struct uses_allocator<experimental::net::v1::executor, Allocator> : true_type {}; } // namespace std
Class executor meets the requirements of Executor ([async.reqmts.executor]), DefaultConstructible (C++ 2014 [defaultconstructible]), and CopyAssignable (C++ 2014 [copyassignable]).
[ Note: To meet the noexcept requirements for executor copy constructors and move constructors, implementations can share a target between two or more executor objects. — end note ]
Postconditions: !*this.
Postconditions: !*this.
executor(const executor& e) noexcept;
Postconditions: !*this if !e; otherwise, *this targets e.target() or a copy of e.target().
executor(executor&& e) noexcept;
Effects: If !e, *this has no target; otherwise, moves e.target() or move-constructs the target of e into the target of *this, leaving e in a valid state with an unspecified value.
template<class Executor> executor(Executor e);
Effects: *this targets a copy of e initialized with std::move(e).
template<class Executor, class ProtoAllocator>
executor(allocator_arg_t, const ProtoAllocator& a, Executor e);
Effects: *this targets a copy of e initialized with std::move(e).
A copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed executor object.
executor& operator=(const executor& e) noexcept;
Effects: executor(e).swap(*this).
Returns: *this.
executor& operator=(executor&& e) noexcept;
Effects: Replaces the target of *this with the target of e, leaving e in a valid state with an unspecified value.
Returns: *this.
executor& operator=(nullptr_t) noexcept;
Effects: executor(nullptr).swap(*this).
Returns: *this.
template<class Executor> executor& operator=(Executor e);
Effects: executor(std::move(e)).swap(*this).
Returns: *this.
Effects: If *this != nullptr, releases shared ownership of, or destroys, the target of *this.
void swap(executor& other) noexcept;
Effects: Interchanges the targets of *this and other.
template<class Executor, class ProtoAllocator>
void assign(Executor e, const ProtoAllocator& a);
Effects: executor(allocator_arg, a, std::move(e)).swap(*this).
execution_context& context() const noexcept;
Requires: *this != nullptr.
Returns: e.context(), where e is the target object of *this.
void on_work_started() const noexcept;
Requires: *this != nullptr.
Effects: e.on_work_started(), where e is the target object of *this.
void on_work_finished() const noexcept;
Requires: *this != nullptr.
Effects: e.on_work_finished(), where e is the target object of *this.
template<class Func, class ProtoAllocator>
void dispatch(Func&& f, const ProtoAllocator& a) const;
Let e be the target object of *this. Let a1 be the allocator that was specified when the target was set. Let fd be the result of DECAY_COPY(f) (C++ 2014 [thread.decaycopy]).
Effects: e.dispatch(g, a1), where g is a function object of unspecified type that, when called as g(), performs fd(). The allocator a is used to allocate any memory required to implement g.
template<class Func, class ProtoAllocator>
void post(Func&& f, const ProtoAllocator& a) const;
Let e be the target object of *this. Let a1 be the allocator that was specified when the target was set. Let fd be the result of DECAY_COPY(f).
Effects: e.post(g, a1), where g is a function object of unspecified type that, when called as g(), performs fd(). The allocator a is used to allocate any memory required to implement g.
template<class Func, class ProtoAllocator>
void defer(Func&& f, const ProtoAllocator& a) const;
Let e be the target object of *this. Let a1 be the allocator that was specified when the target was set. Let fd be the result of DECAY_COPY(f).
Effects: e.defer(g, a1), where g is a function object of unspecified type that, when called as g(), performs fd(). The allocator a is used to allocate any memory required to implement g.
explicit operator bool() const noexcept;
Returns: true if *this has a target, otherwise false.
const type_info& target_type() const noexcept;
Returns: If *this has a target of type T, typeid(T); otherwise, typeid(void).
template<class Executor> Executor* target() noexcept;
template<class Executor> const Executor* target() const noexcept;
Returns: If target_type() == typeid(Executor) a pointer to the stored executor target; otherwise a null pointer value.
bool operator==(const executor& a, const executor& b) noexcept;
bool operator==(const executor& e, nullptr_t) noexcept;
bool operator==(nullptr_t, const executor& e) noexcept;
Returns: !e.
bool operator!=(const executor& a, const executor& b) noexcept;
Returns: !(a == b).
bool operator!=(const executor& e, nullptr_t) noexcept;
bool operator!=(nullptr_t, const executor& e) noexcept;
Returns: (bool) e.
void swap(executor& a, executor& b) noexcept;
Effects: a.swap(b).
[ Note: The function dispatch satisfies the requirements for an asynchronous operation ([async.reqmts.async]), except for the requirement that the operation uses post if it completes immediately. — end note ]
template<class CompletionToken>
DEDUCED dispatch(CompletionToken&& token);
Completion signature: void().
Effects:
Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with token.
Performs ex.dispatch(std::move(completion.completion_handler), alloc), where ex is the result of get_associated_executor(completion.completion_handler), and alloc is the result of get_associated_allocator(completion.completion_handler).
Returns: completion.result.get().
template<class Executor, class CompletionToken>
DEDUCED dispatch(const Executor& ex, CompletionToken&& token);
Completion signature: void().
Effects:
Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with token.
Constructs a function object f containing as members:
a copy of the completion handler h, initialized with std::move(completion.completion_handler),
an executor_work_guard object w for the completion handler's associated executor, initialized with make_work_guard(h),
and where the effect of f() is:
w.get_executor().dispatch(std::move(h), alloc), where alloc is the result of get_associated_allocator(h), followed by
w.reset().
Performs ex.dispatch(std::move(f), alloc), where alloc is the result of get_associated_allocator(completion.completion_handler) prior to the construction of f.
Returns: completion.result.get().
Remarks: This function shall not participate in overload resolution unless is_executor_v<Executor> is true.
template<class ExecutionContext, class CompletionToken>
DEDUCED dispatch(ExecutionContext& ctx, CompletionToken&& token);
Completion signature: void().
Returns: net::dispatch(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.
[ Note: The function post satisfies the requirements for an asynchronous operation ([async.reqmts.async]). — end note ]
template<class CompletionToken>
DEDUCED post(CompletionToken&& token);
Completion signature: void().
Effects:
Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with token.
Performs ex.post(std::move(completion.completion_handler), alloc), where ex is the result of get_associated_executor(completion.completion_handler), and alloc is the result of get_associated_allocator(completion.completion_handler).
Returns: completion.result.get().
template<class Executor, class CompletionToken>
DEDUCED post(const Executor& ex, CompletionToken&& token);
Completion signature: void().
Effects:
Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with token.
Constructs a function object f containing as members:
a copy of the completion handler h, initialized with std::move(completion.completion_handler),
an executor_work_guard object w for the completion handler's associated executor, initialized with make_work_guard(h),
and where the effect of f() is:
w.get_executor().dispatch(std::move(h), alloc), where alloc is the result of get_associated_allocator(h), followed by
w.reset().
Performs ex.post(std::move(f), alloc), where alloc is the result of get_associated_allocator(completion.completion_handler) prior to the construction of f.
Returns: completion.result.get().
Remarks: This function shall not participate in overload resolution unless is_executor_v<Executor> is true.
template<class ExecutionContext, class CompletionToken>
DEDUCED post(ExecutionContext& ctx, CompletionToken&& token);
Completion signature: void().
Returns: net::post(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.
[ Note: The function defer satisfies the requirements for an asynchronous operation ([async.reqmts.async]), except for the requirement that the operation uses post if it completes immediately. — end note ]
template<class CompletionToken>
DEDUCED defer(CompletionToken&& token);
Completion signature: void().
Effects:
Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with token.
Performs ex.defer(std::move(completion.completion_handler), alloc), where ex is the result of get_associated_executor(completion.completion_handler), and alloc is the result of get_associated_allocator(completion.completion_handler).
Returns: completion.result.get().
template<class Executor, class CompletionToken>
DEDUCED defer(const Executor& ex, CompletionToken&& token);
Completion signature: void().
Effects:
Constructs an object completion of type async_completion<CompletionToken, void()>, initialized with token.
Constructs a function object f containing as members:
a copy of the completion handler h, initialized with std::move(completion.completion_handler),
an executor_work_guard object w for the completion handler's associated executor, initialized with make_work_guard(h),
and where the effect of f() is:
w.get_executor().dispatch(std::move(h), alloc), where alloc is the result of get_associated_allocator(h), followed by
w.reset().
Performs ex.defer(std::move(f), alloc), where alloc is the result of get_associated_allocator(completion.completion_handler) prior to the construction of f.
Returns: completion.result.get().
Remarks: This function shall not participate in overload resolution unless is_executor_v<Executor> is true.
template<class ExecutionContext, class CompletionToken>
DEDUCED defer(ExecutionContext& ctx, CompletionToken&& token);
Completion signature: void().
Returns: net::defer(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true.
The class template strand is a wrapper around an object of type Executor satisfying the Executor requirements ([async.reqmts.executor]).
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class Executor> class strand { public: // types: using inner_executor_type = Executor; // [async.strand.cons], construct / copy / destroy: strand(); explicit strand(Executor ex); template<class ProtoAllocator> strand(allocator_arg_t, const ProtoAllocator& alloc, Executor ex); strand(const strand& other) noexcept; strand(strand&& other) noexcept; template<class OtherExecutor> strand(const strand<OtherExecutor>& other) noexcept; template<class OtherExecutor> strand(strand<OtherExecutor>&& other) noexcept; strand& operator=(const strand& other) noexcept; strand& operator=(strand&& other) noexcept; template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other) noexcept; template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other) noexcept; ~strand(); // [async.strand.ops], strand operations: inner_executor_type get_inner_executor() const noexcept; bool running_in_this_thread() const noexcept; execution_context& context() const noexcept; void on_work_started() const noexcept; void on_work_finished() const noexcept; template<class Func, class ProtoAllocator> void dispatch(Func&& f, const ProtoAllocator& a) const; template<class Func, class ProtoAllocator> void post(Func&& f, const ProtoAllocator& a) const; template<class Func, class ProtoAllocator> void defer(Func&& f, const ProtoAllocator& a) const; private: Executor inner_ex_; // exposition only }; bool operator==(const strand<Executor>& a, const strand<Executor>& b); bool operator!=(const strand<Executor>& a, const strand<Executor>& b); } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
strand<Executor> satisfies the Executor ([async.reqmts.executor]) requirements.
A strand provides guarantees of ordering and non-concurrency. Given:
strand objects s1 and s2 such that s1 == s2
a function object f1 added to the strand s1 using post or defer, or using dispatch when s1.running_in_this_thread() is false
a function object f2 added to the strand s2 using post or defer, or using dispatch when s2.running_in_this_thread() is false
then the implementation invokes f1 and f2 such that:
the invocation of f1 is not concurrent with the invocation of f2
the invocation of f1 synchronizes with the invocation of f2.
Furthermore, if the addition of f1 happens before the addition of f2, then the invocation of f1 happens before the invocation of f2.
All member functions, except for the assignment operators and the destructor, do not introduce data races on *this, including its ordered, non-concurrent state. Additionally, constructors and assignment operators do not introduce data races on lvalue arguments.
If any function f executed by the strand throws an exception, the subsequent strand state is as if f had exited without throwing an exception.
Effects: Constructs an object of class strand<Executor> that represents a unique ordered, non-concurrent state. Initializes inner_ex_ as inner_ex_().
Remarks: This overload shall not participate in overload resolution unless Executor satisfies the DefaultConstructible requirements (C++ 2014 [defaultconstructible]).
explicit strand(Executor ex);
Effects: Constructs an object of class strand<Executor> that represents a unique ordered, non-concurrent state. Initializes inner_ex_ as inner_ex_(ex).
template<class ProtoAllocator>
strand(allocator_arg_t, const ProtoAllocator& a, Executor ex);
Effects: Constructs an object of class strand<Executor> that represents a unique ordered, non-concurrent state. Initializes inner_ex_ as inner_ex_(ex). A copy of the allocator argument a is used to allocate memory, if necessary, for the internal data structures of the constructed strand object.
strand(const strand& other) noexcept;
Effects: Initializes inner_ex_ as inner_ex_(other.inner_ex_).
strand(strand&& other) noexcept;
Effects: Initializes inner_ex_ as inner_ex_(std::move(other.inner_ex_)).
template<class OtherExecutor> strand(const strand<OtherExecutor>& other) noexcept;
Requires: OtherExecutor is convertible to Executor.
Effects: Initializes inner_ex_ as inner_ex_(other.inner_ex_).
Postconditions: *this == other.
template<class OtherExecutor> strand(strand<OtherExecutor>&& other) noexcept;
Requires: OtherExecutor is convertible to Executor.
Effects: Initializes inner_ex_ as inner_ex_(std::move(other.inner_ex_)).
Postconditions: *this is equal to the prior value of other.
strand& operator=(const strand& other) noexcept;
Requires: Executor is CopyAssignable (C++ 2014 [copyassignable]).
Returns: *this.
strand& operator=(strand&& other) noexcept;
Requires: Executor is MoveAssignable (C++ 2014 [moveassignable]).
Returns: *this.
template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other) noexcept;
Requires: OtherExecutor is convertible to Executor. Executor is CopyAssignable (C++ 2014 [copyassignable]).
Effects: Assigns other.inner_ex_ to inner_ex_.
Postconditions: *this == other.
Returns: *this.
template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other) noexcept;
Requires: OtherExecutor is convertible to Executor. Executor is MoveAssignable (C++ 2014 [moveassignable]).
Effects: Assigns std::move(other.inner_ex_) to inner_ex_.
Postconditions: *this is equal to the prior value of other.
Returns: *this.
Effects: Destroys an object of class strand<Executor>. After this destructor completes, objects that were added to the strand but have not yet been executed will be executed in a way that meets the guarantees of ordering and non-concurrency.
inner_executor_type get_inner_executor() const noexcept;
Returns: inner_ex_.
bool running_in_this_thread() const noexcept;
Returns: true if the current thread of execution is running a function that was submitted to the strand, or to any other strand object s such that s == *this, using dispatch, post or defer; otherwise false. [ Note: That is, the current thread of execution's call chain includes a function that was submitted to the strand. — end note ]
execution_context& context() const noexcept;
Returns: inner_ex_.context().
void on_work_started() const noexcept;
Effects: Calls inner_ex_.on_work_started().
void on_work_finished() const noexcept;
Effects: Calls inner_ex_.on_work_finished().
template<class Func, class ProtoAllocator>
void dispatch(Func&& f, const ProtoAllocator& a) const;
Effects: If running_in_this_thread() is true, calls DECAY_COPY(forward<Func>(f))() (C++ 2014 [thread.decaycopy]). [ Note: If f exits via an exception, the exception propagates to the caller of dispatch(). — end note ] Otherwise, requests invocation of f, as if by forwarding the function object f and allocator a to the executor inner_ex_, such that the guarantees of ordering and non-concurrency are met.
template<class Func, class ProtoAllocator>
void post(Func&& f, const ProtoAllocator& a) const;
Effects: Requests invocation of f, as if by forwarding the function object f and allocator a to the executor inner_ex_, such that the guarantees of ordering and non-concurrency are met.
template<class Func, class ProtoAllocator>
void defer(Func&& f, const ProtoAllocator& a) const;
Effects: Requests invocation of f, as if by forwarding the function object f and allocator a to the executor inner_ex_, such that the guarantees of ordering and non-concurrency are met.
bool operator==(const strand<Executor>& a, const strand<Executor>& b);
Returns: true, if the strand objects share the same ordered, non-concurrent state; otherwise false.
bool operator!=(const strand<Executor>& a, const strand<Executor>& b);
Returns: !(a == b).
The class template use_future_t defines a set of types that, when passed as a completion token ([async.reqmts.async.token]) to an asynchronous operation's initiating function, cause the result of the asynchronous operation to be delivered via a future (C++ 2014 [futures.uniquefuture]).
namespace std { namespace experimental { namespace net { inline namespace v1 { template<class ProtoAllocator = allocator<void>> class use_future_t { public: // use_future_t types: using allocator_type = ProtoAllocator; // use_future_t members: constexpr use_future_t() noexcept(noexcept(allocator_type())); explicit use_future_t(const allocator_type& a) noexcept; template<class OtherProtoAllocator> use_future_t<OtherProtoAllocator> rebind(const OtherProtoAllocator& a) const noexcept; allocator_type get_allocator() const noexcept; template <class F> unspecified operator()(F&& f) const; }; } // inline namespace v1 } // namespace net } // namespace experimental } // namespace std
constexpr use_future_t() noexcept(noexcept(allocator_type()));
Effects: Constructs a use_future_t with a default-constructed allocator.
explicit use_future_t(const allocator_type& a) noexcept;
Postconditions: get_allocator() == a.
template<class OtherProtoAllocator> use_future_t<OtherProtoAllocator>
rebind(const OtherProtoAllocator& a) const noexcept;
Returns: A use_future_t object where get_allocator() == a.
allocator_type get_allocator() const noexcept;
Returns: The associated allocator object.
template <class F> unspecified operator()(F&& f) const;
Let T be a completion token type. Let H be a completion handler type and let h be an object of type H. Let FD be the type decay_t<F> and let fd be an lvalue of type FD constructed with std::forward<F>(f). Let R(Args...) be the completion signature of an asynchronous operation using H and let N be sizeof...(Args). Let i be in the range [0, N) and let Ai be the ith type in Args. Let ai be the argument associated with Ai.
Returns: A completion token t of type T.
Remarks: The return type T satisfies the Destructible (C++ 2014 [destructible]) and MoveConstructible (C++ 2014 [moveconstructible]) requirements.
The object h of type H is an asynchronous provider with an associated shared state (C++ 2014 [futures.state]). The effect of h(a0, ..., aN-1) is to atomically store the result of INVOKE(fd, forward<A0>(a0), ..., forward<AN-1>(aN-1)) (C++ 2014 [func.require]) in the shared state and make the shared state ready. If fd exits via an exception then that exception is atomically stored in the shared state and the shared state is made ready.
The implementation provides a partial specialization template <class Result, class... Args> async_result<T, Result(Args...)> such that:
the nested type completion_handler_type is a type H;
the nested type return_type is future<result_of_t<FD(decay_t<Args>...)>>; and
when an object r1 of type async_result<T, Result(Args...)> is constructed from h, the expression r1.get() returns a future with the same shared state as h.
For any executor type E, the associated object for the associator associated_executor<H, E> is an executor where, for function objects executed using the executor's dispatch(), post() or defer() functions, any exception thrown is caught by a function object and stored in the associated shared state.
template<class ProtoAllocator, class Result, class... Args> class async_result<use_future_t<ProtoAllocator>, Result(Args...)>{ using completion_handler_type = see below; using return_type = see below; explicit async_result(completion_handler_type& h); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; return_type get(); };
Let R be the type async_result<use_future_t<ProtoAllocator>, Result(Args...)>. Let F be the nested function object type R::completion_handler_type.
An object t1 of type F is an asynchronous provider with an associated shared state (C++ 2014 [futures.state]). The type F provides F::operator() such that the expression t1(declval<Args>()...) is well formed.
The implementation specializes associated_executor for F. For function objects executed using the associated executor's dispatch(), post() or defer() functions, any exception thrown is caught by the executor and stored in the associated shared state.
For any executor type E, the associated object for the associator associated_executor<F, E> is an executor where, for function objects executed using the executor's dispatch(), post() or defer() functions, any exception thrown by a function object is caught by the executor and stored in the associated shared state.
When an object r1 of type R is constructed from t1, the expression r1.get() returns a future with the same shared state as t1.
The type of R::return_type and the effects of F::operator() are defined in Table [tab:async.use.future.result.requirements]. After establishing these effects, F::operator() makes the shared state ready. In this table, N is the value of sizeof...(Args); let i be in the range [0, N) and let Ti be the ith type in Args; let Ui be decay_t<Ti> for each type Ti in Args; let Ai be the deduced type of the ith argument to F::operator(); and let ai be the ith argument to F::operator().
N | U0 | R::return_type | F::operator() effects |
0 | future<void> | None. | |
1 | error_code | future<void> | If a0 evaluates to true, atomically stores the exception pointer produced by make_exception_ptr(system_error(a0)) in the shared state. |
1 | exception_ptr | future<void> | If a0 is non-null, atomically stores the exception pointer a0 in the shared state. |
1 | all other types | future<U0> | Atomically stores forward<A0>(a0) in the shared state. |
2 | error_code | future<U1> | If a0 evaluates to true, atomically stores the exception pointer produced by make_exception_ptr(system_error(a0)) in the shared state; otherwise, atomically stores forward<A1>(a1) in the shared state. |
2 | exception_ptr | future<U1> | If a0 is non-null, atomically stores the exception pointer in the shared state; otherwise, atomically stores forward<A1>(a1) in the shared state. |
2 | all other types | future<tuple<U0, U1>> |
Atomically stores forward_as_tuple( forward<A0>(a0), forward<A1>(a1)) in the shared state. |
>2 | error_code | future<tuple<U1, …, UN-1>> | If a0 evaluates to true, atomically stores the exception pointer produced by make_exception_ptr(system_error(a0)) in the shared state; otherwise, atomically stores forward_as_tuple(forward<A1>(a1), …, forward<AN-1>(aN-1)) in the shared state. |
>2 | exception_ptr | future<tuple<U1, …, UN-1>> | If a0 is non-null, atomically stores the exception pointer in the shared state; otherwise, atomically stores forward_as_tuple(forward<A1>(a1), …, forward<AN-1>(aN-1)) in the shared state. |
>2 | all other types | future<tuple<U0, …, UN-1>> |
Atomically stores forward_as_tuple( forward<A0>(a0), …, forward<AN-1>(aN-1)) in the shared state. |
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {
template<class Result, class... Args, class Signature>
class async_result<packaged_task<Result(Args...)>, Signature>
{
public:
using completion_handler_type = packaged_task<Result(Args...)>;
using return_type = future<Result>;
explicit async_result(completion_handler_type& h);
async_result(const async_result&) = delete;
async_result& operator=(const async_result&) = delete;
return_type get();
private:
return_type future_; // exposition only
};
} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
explicit async_result(completion_handler_type& h);
Effects: Initializes future_ with h.get_future().
return_type get();
Returns: std::move(future_).