Doc. no: D???? (as of commit 730249b70aee6867e05296030cb5dd93482fa509) Date: 2014-??-?? Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>
Table of Contents
<experimental/type_traits> synopsishandler_typeasync_resultasync_completion<experimental/memory> synopsisassociated_allocatorget_associated_allocator<experimental/executor> synopsisexecution_contextexecution_context::serviceis_executoruses_executorassociated_executorget_associated_executorexecutor_wrapperwrapexecutor_workmake_worksystem_executorbad_executorexecutordispatchpostdefer<experimental/strand> synopsisstrand<experimental/timer> synopsisdispatch_atpost_atdefer_atdispatch_afterpost_afterdefer_after<experimental/future> synopsisasync_result for packaged_taskpackaged_handlerpackaged_tokenpackage<experimental/thread_pool> synopsisthread_poolthread_pool::executor_type<experimental/loop_scheduler> synopsisloop_schedulerloop_scheduler::executor_typeThis is a pure library proposal. It does not add any new language features, nor does it alter any existing standard library headers.
This library can be implemented using compilers that conform to the C++14 standard. An implementation of this library requires operating system-specific functions that lie outside the C++14 standard.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class CompletionToken, class Signature, class = void> struct handler_type; template<class CompletionToken, class Signature> using handler_type_t = typename handler_type<CompletionToken, Signature>::type; template<class Handler> class async_result; template<class CompletionToken, class Signature> struct async_completion; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class CompletionToken, class Signature, class = void> struct handler_type { typedef see below type; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Template parameter CompletionToken
specifies the model used to obtain the result of the asynchronous operation.
Template parameter Signature
is the call signature (C++ Std, [func.def]) for the handler type invoked
on completion of the asynchronous operation.
A program may specialize this trait if the CompletionToken
template parameter in the specialization is a user-defined type.
Specializations of handler_type
shall define a nested handler type type
that satisfies the MoveConstructible
requirements, and objects of type type
shall be constructible from an lvalue or rvalue of the type specified by
the CompletionToken template
parameter.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class Handler> class async_result { public: typedef void type; explicit async_result(Handler&); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get(); }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Template argument Handler
is a handler type produced by handler_type_t<T,
S>
for some completion token type T
and call signature S.
A program may specialize this template if the Handler
template parameter in the specialization is a user-defined type.
Specializations of async_result
shall satisfy the Destructible
requirements (C++ Std, [destructible]) in addition to the requirements in
the table below. In this table, R
is a specialization of async_result
for the template parameter Handler;
r is a modifiable lvalue
of type R; and h is a modifiable lvalue of type Handler.
Table 1. async_result specialization requirements
|
Expression |
Return type |
Note |
|---|---|---|
|
|
| |
|
| ||
|
|
|
The |
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class CompletionToken, class Signature> struct async_completion { typedef handler_type_t<CompletionToken, Signature> handler_type; explicit async_completion(remove_reference_t<CompletionToken>& t); async_completion(const async_completion&) = delete; async_completion& operator=(const async_completion&) = delete; see below handler; async_result<handler_type> result; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Template parameter CompletionToken
specifies the model used to obtain the result of the asynchronous operation.
Template parameter Signature
is the call signature (C++ Std, [func.def]) for the handler type invoked
on completion of the asynchronous operation.
explicit async_completion(remove_reference_t<CompletionToken>& t);
Effects: If
CompletionTokenandhandler_typeare the same type, bindshandlertot; otherwise, initializeshandlerwith the result offorward<CompletionToken>(t). Initializesresultwithhandler.
see below handler;
Type:
handler_type&ifCompletionTokenandhandler_typeare the same type; otherwise,handler_type.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Alloc = allocator<void>> struct associated_allocator; template<class T, class Alloc = allocator<void>> using associated_allocator_t = typename associated_allocator<T, Alloc>::type; // get_associated_allocator: template<class T> associated_allocator_t<T> get_associated_allocator(const T& t); template<class T, class Alloc> associated_allocator_t<T, Alloc> get_associated_allocator(const T& t, const Alloc& a); } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Alloc = allocator<void>> struct associated_allocator { typedef see below type; static type get(const T& t, const Alloc& a = Alloc()) noexcept; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
A program may specialize this traits type if the T
template parameter in the specialization is a user-defined type. The template
parameter Alloc shall be
a type meeting Allocator
requirements (C++ Std, [allocator.requirements]).
Specializations of associated_allocator
shall satisfy the requirements in the table below. In this table, X is a specialization of associated_allocator for the template parameter
T; t
is a const reference to an object of type T;
and a is an object of type
Alloc.
Table 2. associated_allocator specialization requirements
|
Expression |
Return type |
Note |
|---|---|---|
|
|
A type meeting | |
|
|
|
Shall not exit via an exception. |
|
|
|
Shall not exit via an exception. |
template<class T> associated_allocator_t<T> get_associated_allocator(const T& t);
Returns:
associated_allocator<T>::get(t).
template<class T, class Alloc> associated_allocator_t<T, Alloc> get_associated_allocator(const T& t, const Alloc& a);
Returns:
associated_allocator<T, Alloc>::get(t, a).
namespace std { namespace experimental { inline namespace concurrency_v1 { 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 : false_type {}; 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 = system_executor> struct associated_executor; template<class T, class Executor = system_executor> using associated_executor_t = typename associated_executor<T, Executor>::type; // get_associated_executor: template<class T> associated_executor_t<T> get_associated_executor(const T& t); template<class T, class Executor> associated_executor_t<T, Executor> get_associated_executor(const T& t, const Executor& ex); template<class T, class ExecutionContext> associated_executor_t<T, typename ExecutionContext::executor_type> get_associated_executor(const T& t, ExecutionContext& ctx); template<class T, class Executor> struct executor_wrapper; template<class T, class Executor, class Signature> struct handler_type<executor_wrapper<T, Executor>, Signature>; template<class T, class Executor> class async_result<executor_wrapper<T, Executor>>; template<class T, class Executor, class Allocator> struct associated_allocator<executor_wrapper<T, Executor>, Allocator>; template<class T, class Executor, class Executor1> struct associated_executor<executor_wrapper<T, Executor>, Executor1>; // wrap: template<class Executor, class T> executor_wrapper<decay_t<T>, Executor> wrap(const Executor& ex, T&& t); template<class ExecutionContext, class T> executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type> wrap(ExecutionContext& ctx, T&& t); template<class T, class Executor> struct executor_work; // make_work: template<class Executor> executor_work<Executor> make_work(const Executor& ex); template<class ExecutionContext> executor_work<typename ExecutionContext::executor_type> make_work(ExecutionContext& ctx); template<class T> executor_work<associated_executor_t<T>> make_work(const T& t); template<class T, class Executor> executor_work<associated_executor_t<T, Executor>> make_work(const T& t, const Executor& ex); template<class T, class ExecutionContext> executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>> make_work(const T& t, ExecutionContext& ctx); class system_executor; bool operator==(const system_executor&, const system_executor&); bool operator!=(const system_executor&, const system_executor&); template<> struct is_executor<system_executor> : true_type {}; class bad_executor; class executor; template <> struct is_executor<executor> : true_type {}; 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; // dispatch: template<class CompletionToken> auto dispatch(CompletionToken&& token); template<class Executor, class CompletionToken> auto dispatch(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> auto dispatch(ExecutionContext& ctx, CompletionToken&& token); // post: template<class CompletionToken> auto post(CompletionToken&& token); template<class Executor, class CompletionToken> auto post(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> auto post(ExecutionContext& ctx, CompletionToken&& token); // defer: template<class CompletionToken> auto defer(CompletionToken&& token); template<class Executor, class CompletionToken> auto defer(const Executor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> auto defer(ExecutionContext& ctx, CompletionToken&& token); } // inline namespace concurrency_v1 } // namespace experimental template<class Alloc> struct uses_allocator<std::experimental::concurrency_v1::executor, Alloc> : true_type {}; } // namespace std
The library describes a standard set of requirements for executors. A type meeting Executor requirements shall embody a set of rules for determining how submitted function objects are to be executed.
An executor type X shall
satisfy the requirements of CopyConstructible
(C++ Std, [copyconstructible]) types. No constructor, 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 member functions defined in these requirements shall not introduce data races as a result of concurrent calls to those functions from different threads.
In the table below, X denotes
an executor class, x denotes
a value of type X&,
x1 and x2
denote values of type const X&,
x3 denotes a value of type
X&&,
f denotes a MoveConstructible (C++ Std, [moveconstructible])
function object callable with zero arguments, a
denotes a value of type A
meeting Allocator requirements
(C++ Std, [allocator.requirements]), t
denotes an object of type T,
and u denotes an identifier.
Table 3. Executor requirements
|
expression |
type |
assertion/note |
|---|---|---|
|
|
Shall not exit via an exception. | |
|
|
Shall not exit via an exception. | |
|
|
|
Shall not exit via an exception. |
|
|
|
Shall not exit via an exception. |
|
|
|
Shall not exit via an exception. |
|
|
Shall not exit via an exception. | |
|
|
Shall not exit via an exception. | |
|
|
Effects: Calls | |
|
|
Effects: Calls | |
|
|
Effects: Calls |
A class is a service if it is publicly derived from another service, or
if it is a class publicly derived from execution_context::service.
A service may contain a publicly-accessible nested typedef named key_type. If the nested typedef key_type exists, the service class shall
be the same type as key_type,
or otherwise publicly and unambiguously derived from key_type.
All services define a one-argument constructor that takes a reference to
the execution_context object
that owns the service. This constructor is explicit,
preventing its participation in automatic conversions.
A service may provide additional constructors with two or more arguments,
where the first argument is a reference to the execution_context
object that owns the service. [Note: These constructors
may be called by the make_service
function. —end note]
[Example:
class my_service : public execution_context::service { public: typedef my_service key_type; explicit my_service(execution_context& ctx); private: virtual void shutdown_service(); ... };
—end example]
A service's shutdown_service
member function must cause all copies of user-defined function objects
that are held by the service to be destroyed.
namespace std { namespace experimental { inline namespace concurrency_v1 { class execution_context { public: class service; // construct / copy / destroy: execution_context(); execution_context(const execution_context&) = delete; execution_context& operator=(const execution_context&) = delete; virtual ~execution_context(); // execution context operations: void notify_fork(fork_event e); protected: // execution context protected operations: void shutdown_context(); void destroy_context(); }; // service access: 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; class service_already_exists : public logic_error { ... }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Class execution_context implements
an extensible, type-safe, polymorphic set of services, indexed by service
type.
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,
making available all members of the named type. If the service is not present
in an execution_context,
an object of type Service
is created and added to the execution_context.
A C++ 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,
the service_already_exists
exception is thrown.
Once a service reference is obtained from an execution_context
object by calling use_service<>, that reference remains usable until
a call to destroy_context().
execution_context();
Effects: Creates an object of class
execution_context.
~execution_context();
Effects: Destroys an object of class
execution_context. Performsshutdown_context()followed bydestroy_context().
void notify_fork(fork_event e);
Effects: For each service object
svcin the set:
— Ife == fork_event::prepare, performssvc->notify_fork(e)in reverse order of the beginning of service object lifetime (C++ Std, [basic.life]).
— Otherwise, performssvc->notify_fork(e)in order of the beginning of service object lifetime.
void shutdown_context();
Effects: For each service object
svcin theexecution_contextset, in reverse order of the beginning of service object lifetime (C++ Std, [basic.life]), performssvc->shutdown_service().
[Note:
shutdown_contextis an idempotent operation. —end note]
void destroy_context();
Effects: Destroys each service object in the
execution_contextset, in reverse order of the beginning of service object lifetime (C++ Std, [basic.life]).
[Note:
destroy_contextis an idempotent operation. —end note]
The functions use_service,
make_service and has_service shall not introduce data
races as a result of concurrent calls to those functions from different
threads.
template<class Service> Service& use_service(execution_context& ctx);
Let
KeybeService::key_typeif the nested typedefService::key_typeexists; otherwise, letKeybeService.
Requires:
Serviceis a service class that is publicly and unambiguously derived fromexecution_context::service. If the nested typedefService::key_typeexists,Serviceis the same type asService::key_type, orServiceis publicly and unambiguously derived fromService::key_type, andService::key_typeis publicly and unambiguously derived fromexecution_context::service.
Effects: If an object of type
Keydoes not already exist in theexecution_contextset identified byctx, creates an object of typeService, initializing it withService(ctx), and adds it to the set.
Returns: A reference to the corresponding service of
ctx.
Notes: The reference returned remains valid until a call to
destroy_context.
template<class Service, class... Args> Service& make_service(execution_context& ctx, Args&&... args);
Let
KeybeService::key_typeif the nested typedefService::key_typeexists; otherwise, letKeybeService.
Requires:
Serviceis a service class that is publicly and unambiguously derived fromexecution_context::service. If the nested typedefService::key_typeexists,Serviceis the same type asService::key_type, orServiceis publicly and unambiguously derived fromService::key_type, andService::key_typeis publicly and unambiguously derived fromexecution_context::service. A service object of typeKeydoes not already exist in theexecution_contextset identified byctx.
Effects: Creates an object of type
Service, initializing it withService(ctx, forward<Args>(args)...), and adds it to theexecution_contextset identified byctx.
Throws:
service_already_existsif a corresponding service object of typeKeyis already present in the set.
Notes: The reference returned remains valid until a call to
destroy_context.
template<class Service> bool has_service(execution_context& ctx) noexcept;
Let
KeybeService::key_typeif the nested typedefService::key_typeexists; otherwise, letKeybeService.
Requires:
Serviceis a service class that is publicly and unambiguously derived fromexecution_context::service. If the nested typedefService::key_typeexists,Serviceis the same type asService::key_type, orServiceis publicly and unambiguously derived fromService::key_type, andService::key_typeis publicly and unambiguously derived fromexecution_context::service.
Returns: If an object of type
Keyis present inctx,true; otherwise,false.
namespace std { namespace experimental { inline namespace concurrency_v1 { class execution_context::service { protected: // construct / copy / destroy: service(execution_context& owner); service(const service&) = delete; service& operator=(const service&) = delete; virtual ~service(); // service observers: execution_context& context() noexcept; private: friend class execution_context; // exposition only // service operations: virtual void shutdown_service() = 0; virtual void notify_fork(fork_event e); execution_context& context_; // exposition only }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
service(execution_context& owner);
Postconditions:
&context_ == &owner.
execution_context& context() noexcept;
Returns:
context_.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T> struct is_executor : false_type {}; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
is_executor can be used to
detect executor types satisfying the Executor
type requirements.
Instantiations of the is_executor
template shall meet the UnaryTypeTrait requirements (C++ Std, [meta.rqmts]).
A program may specialize this template for a user-defined type T to have a BaseCharacteristic of integral_constant<int, N> with N
> 0 to indicate that T
should be treated as an executor type.
namespace std { namespace experimental { inline namespace concurrency_v1 { struct executor_arg_t { }; constexpr executor_arg_t executor_arg = executor_arg_t(); } // inline namespace concurrency_v1 } // 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.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Executor> struct uses_executor; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Remark: Detects whether T
has a nested executor_type
that is convertible from Executor.
Meets the BinaryTypeTrait
requirements (C++ Std, [meta.rqmts]). The implementation shall provide
a definition that 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 where either:
— the first argument of a constructor has type executor_type
and the second argument has type Executor;
or
— the last argument of a constructor 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<T, Executor>::value
is false and is_constructible<T, V1, V2, ..., VN>::value is true, then obj
is initialized as obj(v1, v2, ..., vN);
— otherwise, if uses_executor<T, Executor>::value
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, if uses_executor<T, Executor>::value
is true and is_constructible<T, V1, V2, ..., VN, Executor>::value is true, then obj
is initialized as obj(v1, v2, ..., vN, ex);
— otherwise, the request for uses-executor construction is ill-formed. [Note:
An error will result if uses_executor<T, Executor>::value
is true but the specific constructor does not take an executor. This definition
prevents a silent failure to pass the executor to an element. —end
note]
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Executor = system_executor> struct associated_executor { typedef see below type; static type get(const T& t, const Executor& e = Executor()) noexcept; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
A program may specialize this traits type if the T
template parameter in the specialization is a user-defined type. The template
parameter Executor shall
be a type meeting Executor requirements.
Specializations of associated_executor
shall satisfy the requirements in the table below. In this table, X is a specialization of associated_executor for the template parameter
T; t
is a const reference to an object of type T;
and e is an object of type
Executor.
Table 4. associated_executor specialization requirements
|
Expression |
Return type |
Note |
|---|---|---|
|
|
A type meeting Executor requirements. | |
|
|
|
Shall not exit via an exception. |
|
|
|
Shall not exit via an exception. |
template<class T> associated_executor_t<T> get_associated_executor(const T& t);
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);
Returns:
associated_executor<T, Executor>::get(t, ex).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class T, class ExecutionContext> associated_executor_t<T, typename ExecutionContext::executor_type> get_associated_executor(const T& t, ExecutionContext& ctx);
Returns:
associated_executor<T, typename ExecutionContext::executor_type>::get(t, ctx.get_executor()).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Executor> class executor_wrapper { public: // types: typedef T wrapped_type; typedef Executor executor_type; typedef see below result_type; // not always defined typedef see below argument_type; // not always defined typedef see below first_argument_type; // not always defined typedef see below second_argument_type; // not always defined // construct / copy / destroy: executor_wrapper(T t, const Executor& ex); executor_wrapper(const executor_wrapper& other) = default; executor_wrapper(executor_wrapper&& other) = default; template<class U, class OtherExecutor> executor_wrapper(const executor_wrapper<U, OtherExecutor>& other); template<class U, class OtherExecutor> executor_wrapper(executor_wrapper<U, OtherExecutor>&& other); template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, const executor_wrapper<U, OtherExecutor>& other); template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, executor_wrapper<U, OtherExecutor>&& other); ~executor_wrapper(); // executor wrapper access: T& unwrap() noexcept; const T& unwrap() const noexcept; executor_type get_executor() const noexcept; // executor wrapper invocation: template<class... Args> result_of_t<cv T&(Args&&...)> operator()(Args&&... args) cv; private: Executor ex_; // exposition only T wrapped_; // exposition only }; template<class T, class Executor, class Signature> struct handler_type<executor_wrapper<T, Executor>, Signature> { typedef executor_wrapper<handler_type_t<T, Signature>, Executor> type; }; template<class T, class Executor> class async_result<executor_wrapper<T, Executor>>; template<class T, class Executor, class Alloc> struct associated_allocator<executor_wrapper<T, Executor>, Alloc>; template<class T, class Executor, class Executor1> struct associated_executor<executor_wrapper<T, Executor>, Executor1>; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
executor_wrapper<T, Executor> is a wrapper around an object or function
of type T, and an executor
object of type Executor satisfying
Executor requirements.
executor_wrapper<T, Executor> has a weak result type (C++ Std, [func.require]).
If T is a function type,
result_type shall be a synonym
for the return type of T.
The template instantiation executor_wrapper<T,
Executor>
shall define a nested type named argument_type
as a synonym for T1 only
if the type T is any of the
following:
— a function type or a pointer to function type taking one argument of type
T1
— a pointer to member function R
T0::f cv
(where cv represents the member function’s
cv-qualifiers); the type T1
is cv T0*
— a class type with a member type argument_type;
the type T1 is T::argument_type.
The template instantiation executor_wrapper<T,
Executor>
shall define two nested types named first_argument_type
and second_argument_type
as synonyms for T1 and T2, respectively, only if the type T is any of the following:
— a function type or a pointer to function type taking two arguments of types
T1 and T2
— a pointer to member function R
T0::f(T2) cv
(where cv represents the member function’s
cv-qualifiers); the type T1
is cv T0*
— a class type with member types first_argument_type
and second_argument_type;
the type T1 is T::first_argument_type.
and the type T2 is T::second_argument_type.
executor_wrapper(T t, const Executor& ex);
Effects: Constructs an object of type
executor_wrapper<T, Executor>. Initializesex_with the valueex. Ifuses_executor<T, Executor>::valueis true, performs uses-executor construction to initializewrapped_withwrapped_(executor_arg, ex_, std::move(t)); otherwise, initializeswrapped_withwrapped_(std::move(t)).
template<class U, class OtherExecutor> executor_wrapper(const executor_wrapper<U, OtherExecutor>& other);
Requires:
UisTor convertible toT.OtherExecutorisExecutoror convertible toExecutor.
Effects: Constructs an object of type
executor_wrapper<T, Executor>. Initializesex_withother.get_executor(). Ifuses_executor<T, Executor>::valueis true, performs uses-executor construction to initializewrapped_withwrapped_(executor_arg, ex_, other.unwrap()); otherwise, initializeswrapped_withwrapped_(other.unwrap()).
template<class U, class OtherExecutor> executor_wrapper(executor_wrapper<U, OtherExecutor>&& other);
Requires:
UisTor convertible toT.OtherExecutorisExecutoror convertible toExecutor.
Effects: Constructs an object of type
executor_wrapper<T, Executor>. Initializesex_withother.get_executor(). Ifuses_executor<T, Executor>::valueis true, performs uses-executor construction to initializewrapped_withwrapped_(executor_arg, ex_, std::move(other.unwrap())); otherwise, initializeswrapped_withwrapped_(std::move(other.unwrap())).
template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, const executor_wrapper<U, OtherExecutor>& other);
Requires:
UisTor convertible toT.
Effects: Constructs an object of type
executor_wrapper<T, Executor>. Initializesex_withex. Ifuses_executor<T, Executor>::valueis true, performs uses-executor construction to initializewrapped_withwrapped_(executor_arg, ex_, other.unwrap()); otherwise, initializeswrapped_withwrapped_(other.unwrap()).
template<class U, class OtherExecutor> executor_wrapper(executor_arg_t, const Executor& ex, executor_wrapper<U, OtherExecutor>&& other);
Requires:
UisTor convertible toT.
Effects: Constructs an object of type
executor_wrapper<T, Executor>. Initializesex_withex. Ifuses_executor<T, Executor>::valueis true, performs uses-executor construction to initializewrapped_withwrapped_(executor_arg, ex_, std::move(other.unwrap())); otherwise, initializeswrapped_withwrapped_(std::move(other.unwrap())).
T& unwrap() noexcept; const T& unwrap() const noexcept;
Returns:
wrapped_.
executor_type get_executor() const noexcept;
Returns:
executor_.
template<class... Args> result_of_t<cv T&(Args&&...)> operator()(Args&&... args) cv;
Returns:
INVOKE(unwrap(), forward<Args>(args)...)(C++ Std, [func.require]).
Remarks:
operator()is described for exposition only. Implementations are not required to provide an actualexecutor_wrapper::operator(). Implementations are permitted to supportexecutor_wrapperfunction invocation through multiple overloaded operators or through other means.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Executor> class async_result<executor_wrapper<T, Executor>> { public: typedef typename async_result<T>::type type; explicit async_result(executor_wrapper<T, Executor>& wrapper); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get(); private: async_result<T> wrapped_; // exposition only }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
The implementation shall provide a specialization of async_result
that meets the async_result
specialization requirements.
explicit async_result(executor_wrapper<T, Executor>& wrapper);
Effects: Initializes
wrapped_withwrapped_(wrapper.unwrap()).
type get();
Returns:
wrapped_.get().
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Executor, class Alloc> struct associated_allocator<executor_wrapper<T, Executor>, Alloc> { typedef associated_allocator_t<T, Alloc> type; static type get(const executor_wrapper<T, Executor>& w, const Alloc& a = Alloc()) noexcept; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
The implementation shall provide a specialization of associated_allocator
that meets the associated_allocator specialization requirements.
static type get(const executor_wrapper<T, Executor>& w, const Alloc& a = Alloc()) noexcept;
Returns:
associated_allocator<T, Alloc>::get(w.unwrap(), a).
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class T, class Executor, class Executor1> struct associated_executor<executor_wrapper<T, Executor>, Executor1> { typedef Executor type; static type get(const executor_wrapper<T, Executor>& w, const Executor1& e = Executor1()) noexcept; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
The implementation shall provide a specialization of associated_executor
that meets the associated_executor specialization requirements.
static type get(const executor_wrapper<T, Executor>& w, const Executor1& e = Executor1()) noexcept;
Returns:
w.get_executor().
template<class Executor, class T> executor_wrapper<decay_t<T>, Executor> wrap(const Executor& ex, T&& t);
Returns:
executor_wrapper<decay_t<T>, Executor>(forward<T>(t), ex).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class ExecutionContext, class CompletionToken> executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type> wrap(ExecutionContext& ctx, T&& t);
Returns:
executor_wrapper<decay_t<T>, typename ExecutionContext::executor_type>(forward<T>(t), ctx.get_executor()).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class Executor> class executor_work { public: // types: typedef Executor executor_type; // construct / copy / destroy: explicit executor_work(const executor_type& ex) noexcept; executor_work(const executor_work& other) noexcept; executor_work(executor_work&& other) noexcept; executor_work operator=(const executor_type&) = delete; ~executor_work(); // executor work observers: executor_type get_executor() const noexcept; bool owns_work() const noexcept; // executor work modifiers: void reset() noexcept; private: Executor ex_; // exposition only bool owns_; // exposition only }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
explicit executor_work(const executor_type& ex) noexcept;
Effects: Constructs an object of class
executor_work, initializingex_withex, and then performingex_.on_work_started().
Postconditions:
ex == ex_andowns_ == true.
executor_work(const executor_work& other) noexcept;
Effects: Constructs an object of class
executor_work, initializingex_withother.ex. Ifother.owns_ == true, performsex_.on_work_started().
Postconditions:
ex == other.ex_andowns_ == other.owns_.
executor_work(executor_work&& other) noexcept;
Effects: Constructs an object of class
executor_work, initializingex_withother.exandowns_withother.owns_.
Postconditions:
exis equal to the prior value ofother.ex_,owns_is equal to the prior value ofother.owns_, andother.owns_ == false.
executor_type get_executor() const noexcept;
Returns:
ex_.
bool owns_work() const noexcept;
Returns:
owns_.
template<class Executor> executor_work<Executor> make_work(const Executor& ex);
Returns:
executor_work<Executor>(ex).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class ExecutionContext> executor_work<typename ExecutionContext::executor_type> make_work(ExecutionContext& ctx);
Returns: An object of type
executor_work<typename ExecutionContext::executor_type>initialized with the result ofctx.get_executor().
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class T> executor_work<associated_executor_t<T>> make_work(const T& t);
Returns: An object of type
executor_work<associated_executor_t<T>>initialized with the result ofassociated_executor<T>::get(t).
Remarks: This function shall not participate in overload resolution unless
is_executor<T>::valueis false andis_convertible<T&, execution_context&>::valueis false.
template<class T, class Executor> executor_work<associated_executor_t<T, Executor>> make_work(const T& t, const Executor& ex);
Returns: An object of type
executor_work<associated_executor_t<T, Executor>>initialized with the result ofassociated_executor<T, Executor>::get(t, ex).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class T, class ExecutionContext> executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>> make_work(const T& t, ExecutionContext& ctx);
Returns: An object of type
executor_work<associated_executor_t<T, typename ExecutionContext::executor_type>>initialized with the result ofassociated_executor<T, typename ExecutionContext::executor_type>::get(t, ctx.get_executor()).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
namespace std { namespace experimental { inline namespace concurrency_v1 { class system_executor { public: // executor operations: execution_context& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a); template<class Func, class Alloc> void post(Func&& f, const Alloc& a); template<class Func, class Alloc> void defer(Func&& f, const Alloc& a); }; bool operator==(const system_executor&, const system_executor&) noexcept; bool operator!=(const system_executor&, const system_executor&) noexcept; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Class system_executor is
a DefaultConstructible type
(C++ Std, [defaultconstructible]) satisfying Executor
requirements. It represents a set of rules where function objects
are permitted to execute on any thread.
To satisfy the executor requirements for the post
and defer member functions,
the system executor may allocate thread
objects to run the submitted function objects. If std::exit
is called, and there remain unexecuted functions objects that have been submitted
using post or defer, the implementation shall discard
these function objects without calling them.
execution_context& context() noexcept;
Returns: A reference to a static-duration object of a type derived from
execution_context.
void on_work_started() noexcept;
Effects: Does nothing.
void on_work_finished() noexcept;
Effects: Does nothing.
template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a);
Effects: Calls
DECAY_COPY(forward<Func>(f))().
template<class Func, class Alloc> void post(Func&& f, const Alloc& a);
Effects: Calls
DECAY_COPY(forward<Func>(f))()as if in a thread of execution represented by athreadobject, with the call toDECAY_COPY()being evaluated in the thread that calledpost. Any exception propagated from the execution ofDECAY_COPY(forward<Func>(f))()shall result in a call tostd::terminate.
template<class Func, class Alloc> void defer(Func&& f, const Alloc& a);
Effects: Calls
DECAY_COPY(forward<Func>(f))()as if in a thread of execution represented by athreadobject, with the call toDECAY_COPY()being evaluated in the thread that calleddefer. Any exception propagated from the execution ofDECAY_COPY(forward<Func>(f))()shall result in a call tostd::terminate.
namespace std { namespace experimental { inline namespace concurrency_v1 { class bad_executor : public exception { public: // constructor: bad_executor() noexcept; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
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 { inline namespace concurrency_v1 { class executor { public: // 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 Alloc> executor(allocator_arg_t, const Alloc& 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(); // executor modifiers: void swap(executor& other) noexcept; template<class Executor, class Alloc> void assign(Executor e, const Alloc& e); // executor operations: execution_context& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a); template<class Func, class Alloc> void post(Func&& f, const Alloc& a); template<class Func, class Alloc> void defer(Func&& f, const Alloc& a); // executor capacity: explicit operator bool() const noexcept; // executor target access: const type_info& target_type() const noexcept; template<class Executor> Executor* target() noexcept; template<class Executor> const Executor* target() const noexcept; }; template<> struct is_executor<executor> : true_type {}; // 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; // executor specialized algorithms: void swap(executor& a, executor& b) noexcept; } // inline namespace concurrency_v1 } // namespace experimental template<class Alloc> struct uses_allocator<std::experimental::concurrency_v1::executor, Alloc> : true_type {}; } // namespace std
The executor class provides
a polymorphic wrapper for types that satisfy the Executor
requirements. The target object is the executor
object that is held by the wrapper. The executor
type itself meets the requirements for an Executor.
[Note: To meet the noexcept
requirements for executor copy constructors and move constructors, implementations
may share a target between two or more executor
objects. —end note]
executor() noexcept;
Postconditions:
!*this.
executor(nullptr_t) noexcept;
Postconditions:
!*this.
executor(const executor& e) noexcept;
Postconditions:
!*thisif!e; otherwise,*thistargetse.target()or a copy ofe.target().
executor(executor&& e) noexcept;
Effects: If
!e,*thishas no target; otherwise, movese.target()or move-constructs the target ofeinto the target of*this, leavingein a valid state with an unspecified value.
template<class Executor> executor(Executor e);
Requires:
Executorshall satisfy the Executor requirements.
Effects:
*thistargets a copy ofeinitialized withstd::move(e).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Executor, class Alloc> executor(allocator_arg_t, const Alloc& a, Executor e);
Requires:
Executorshall satisfy the Executor requirements.Allocatorconforms to theAllocatorrequirements (C++ Std, [allocator.requirements]).
Effects:
*thistargets a copy ofeinitialized withstd::move(e).
A copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed
executorobject.
executor& operator=(const executor& e) noexcept;
Effects:
executor(e).swap(*this).
Returns:
*this.
executor& operator=(executor&& e) noexcept;
Effects: Replaces the target of
*thiswith the target ofe, leavingein 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.
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
~executor();
Effects: If
*this != nullptr, releases shared ownership of, or destroys, the target of*this.
void swap(executor& other) noexcept;
Effects: Interchanges the targets of
*thisandother.
template<class Executor, class Alloc> void assign(Executor e, const Alloc& e);
Effects:
executor(allocator_arg, a, std::move(e)).swap(*this).
execution_context& context() noexcept;
Returns:
e.context(), whereeis the target object of*this.
void on_work_started() noexcept;
Effects:
e.on_work_started(), whereeis the target object of*this.
void on_work_finished() noexcept;
Effects:
e.on_work_finished(), whereeis the target object of*this.
template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a);
Effects:
e.dispatch(g, a), whereeis the target object of*this, andgis a function object of unspecified type that, when called asg(), performsDECAY_COPY(f)().
template<class Func, class Alloc> void post(Func&& f, const Alloc& a);
Effects:
e.post(g, a), whereeis the target object of*this, andgis a function object of unspecified type that, when called asg(), performsDECAY_COPY(f)().
template<class Func, class Alloc> void defer(Func&& f, const Alloc& a);
Effects:
e.defer(g, a), whereeis the target object of*this, andgis a function object of unspecified type that, when called asg(), performsDECAY_COPY(f)().
explicit operator bool() const noexcept;
Returns:
trueif*thishas a target, otherwisefalse,
const type_info& target_type() const noexcept;
Returns: If
*thishas a target of typeT,typeid(T); otherwise,typeid(void).
template<class Executor> Executor* target() noexcept; template<class Executor> const Executor* target() const noexcept;
Requires:
Executorshall satisfy the Executor requirements.
Returns: If
target_type() == typeid(Executor)a pointer to the stored executor target; otherwise a null pointer.
bool operator==(const executor& a, const executor& b) noexcept;
Returns:
—trueif!aand!b;
—trueifaandbshare a target;
—trueifeandfare the same type ande == f, whereeis the target object ofaandfis the target object ofb;
— otherwisefalse.
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.
template<class CompletionToken> auto dispatch(CompletionToken&& token);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectexby performingget_associated_executor(handler).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Performsex.dispatch(std::move(handler), alloc).
Returns:
result.get().
template<class Executor, class CompletionToken> auto dispatch(const Executor& ex, CompletionToken&& token);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectex1by performingget_associated_executor(handler).
— Creates a work objectwby performingmake_work(ex1).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Constructs a function objectfwith a function call operator that performsex1.dispatch(std::move(handler), alloc)followed byw.reset().
— Performsex.dispatch(std::move(f), alloc).
Returns:
result.get().
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class ExecutionContext, class CompletionToken> auto dispatch(ExecutionContext& ctx, CompletionToken&& token);
Returns:
std::experimental::concurrency_v1::dispatch(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class CompletionToken> auto post(CompletionToken&& token);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectexby performingget_associated_executor(handler).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Performsex.post(std::move(handler), alloc).
Returns:
result.get().
template<class Executor, class CompletionToken> auto post(const Executor& ex, CompletionToken&& token);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectex1by performingget_associated_executor(handler).
— Creates a work objectwby performingmake_work(ex1).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Constructs a function objectfwith a function call operator that performsex1.dispatch(std::move(handler), alloc)followed byw.reset().
— Performsex.post(std::move(f), alloc).
Returns:
result.get().
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class ExecutionContext, class CompletionToken> auto post(ExecutionContext& ctx, CompletionToken&& token);
Returns:
std::experimental::concurrency_v1::post(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class CompletionToken> auto defer(CompletionToken&& token);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectexby performingget_associated_executor(handler).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Performsex.defer(std::move(handler), alloc).
Returns:
result.get().
template<class Executor, class CompletionToken> auto defer(const Executor& ex, CompletionToken&& token);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectex1by performingget_associated_executor(handler).
— Creates a work objectwby performingmake_work(ex1).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Constructs a function objectfwith a function call operator that performsex1.dispatch(std::move(handler), alloc)followed byw.reset().
— Performsex.defer(std::move(f), alloc).
Returns:
result.get().
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class ExecutionContext, class CompletionToken> auto defer(ExecutionContext& ctx, CompletionToken&& token);
Returns:
std::experimental::concurrency_v1::defer(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
namespace std { namespace experimental { inline namespace concurrency_v1 { 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 Executor> struct is_executor<strand<Executor>> : true_type {}; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace concurrency_v1 { template<class Executor> class strand { public: // types: typedef Executor inner_executor_type; // construct / copy / destroy: strand(); explicit strand(Executor ex); strand(const strand& other); strand(strand&& other); template<class OtherExecutor> strand(const strand<OtherExecutor>& other); template<class OtherExecutor> strand(strand<OtherExecutor>&& other); strand& operator=(const strand& other); strand& operator=(strand&& other); template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other); template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other); ~strand(); // strand operations: inner_executor_type get_inner_executor() const noexcept; bool running_in_this_thread() const noexcept; execution_context& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a); template<class Func, class Alloc> void post(Func&& f, const Alloc& a); template<class Func, class Alloc> void defer(Func&& f, const Alloc& a); 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 concurrency_v1 } // namespace experimental } // namespace std
strand<Executor>
is a wrapper around an object of type Executor
satisfying Executor requirements.
strand<Executor>
satisfies the 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() == false
— a function object f2 added
to the strand s2 using post or defer,
or using dispatch when s2.running_in_this_thread() == false
then the implementation shall invoke 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.
The strand copy constructors, comparison operators, and member functions shall not introduce data races as a result of concurrent calls to those functions from different threads.
If any function f executed
by the strand throws an exception, the subsequent strand state shall be as
if f had exited without throwing
an exception.
strand();
Effects: Constructs an object of class
strand<Executor>that represents a unique ordered, non-concurrent state. Initializesinner_ex_withinner_ex_().
Remarks: This overload shall not participate in overload resolution unless
Executorsatisfies theDefaultConstructiblerequirements (C++ Std, [defaultconstructible]).
explicit strand(Executor ex);
Effects: Constructs an object of class
strand<Executor>that represents a unique ordered, non-concurrent state. Initializesinner_ex_withinner_ex_(ex).
strand(const strand& other);
Effects: Constructs an object of class
strand<Executor>. Initalizesinner_ex_withinner_ex_(other.inner_ex_).
Postconditions:
—*this == other
—get_inner_executor() == other.get_inner_executor()
strand(strand&& other);
Effects: Constructs an object of class
strand<Executor>. Initalizesinner_ex_withinner_ex_(std::move(other.inner_ex_)).
Postconditions:
—*thisis equal to the prior value ofother
—get_inner_executor() == other.get_inner_executor()
template<class OtherExecutor> strand(const strand<OtherExecutor>& other);
Requires:
OtherExecutoris convertible toExecutor.
Effects: Constructs an object of class
strand<Executor>. Initalizesinner_ex_withinner_ex_(other.inner_ex_).
Postconditions:
*this == other.
template<class OtherExecutor> strand(strand<OtherExecutor>&& other);
Requires:
OtherExecutoris convertible toExecutor.
Effects: Constructs an object of class
strand<Executor>. Initalizesinner_ex_withinner_ex_(other.inner_ex_).
Postconditions:
*thisis equal to the prior value ofother.
strand& operator=(const strand& other);
Requires:
ExecutorisAssignable(C++ Std [assignable]).
Postconditions:
—*this == other
—get_inner_executor() == other.get_inner_executor()
Returns:
*this.
strand& operator=(strand&& other);
Requires:
ExecutorisAssignable(C++ Std [assignable]).
Postconditions:
—*thisis equal to the prior value ofother
—get_inner_executor() == other.get_inner_executor()
Returns:
*this.
template<class OtherExecutor> strand& operator=(const strand<OtherExecutor>& other);
Requires:
OtherExecutoris convertible toExecutor.ExecutorisAssignable(C++ Std [assignable]).
Effects: Equivalent to
strand<Executor>::operator=(Executor(other)).
Returns:
*this.
template<class OtherExecutor> strand& operator=(strand<OtherExecutor>&& other);
Requires:
OtherExecutoris convertible toExecutor.ExecutorisAssignable(C++ Std [assignable]).
Effects: Equivalent to
strand<Executor>::operator=(Executor(std::move(other))).
Returns:
*this.
~strand();
Effects: Destroys an object of class
strand<Executor>. Function objects that were added to the strand but have not yet been executed shall still 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:
trueif the current thread of execution is invoking a function that was submitted to the strand, or to any other strand objectssuch thats == *this, usingdispatch,postordefer; otherwisefalse.
execution_context& context() noexcept;
Returns:
inner_ex_.context().
void on_work_started() noexcept;
Effects: Calls
inner_ex_.on_work_started().
void on_work_finished() noexcept;
Effects: Calls
inner_ex_.on_work_finished().
template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a);
Effects: If
running_in_this_thread()is true, callsDECAY_COPY(forward<Func>(f))(). Otherwise, requests invocation off, as if by forwarding the function objectfand allocatorato the executorinner_ex_, such that the guarantees of ordering and non-concurrency are met.
If
fexits via an exception, and the execution offis performed in the current thread and beforedispatchreturns, the exception shall propagate to the caller ofdispatch()
template<class Func, class Alloc> void post(Func&& f, const Alloc& a);
Effects: Requests invocation of
f, as if by forwarding the function objectfand allocatorato the executorinner_ex_, such that the guarantees of ordering and non-concurrency are met.
template<class Func, class Alloc> void defer(Func&& f, const Alloc& a);
Effects: Requests invocation of
f, as if by forwarding the function objectfand allocatorato the executorinner_ex_, such that the guarantees of ordering and non-concurrency are met.
#include <chrono> namespace std { namespace experimental { inline namespace concurrency_v1 { // dispatch_at: template<class Clock, class Duration, class... CompletionTokens> auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time, CompletionTokens&&... tokens); template<class Clock, class Duration, class Executor, class... CompletionTokens> auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time, const Executor& ex, CompletionTokens&&... tokens); template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens> auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time, ExecutionContext& ctx, CompletionTokens&&... tokens); // post_at: template<class Clock, class Duration, class... CompletionTokens> auto post_at(const chrono::time_point<Clock, Duration>& abs_time, CompletionTokens&&... tokens); template<class Clock, class Duration, class Executor, class... CompletionTokens> auto post_at(const chrono::time_point<Clock, Duration>& abs_time, const Executor& ex, CompletionTokens&&... tokens); template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens> auto post_at(const chrono::time_point<Clock, Duration>& abs_time, ExecutionContext& ctx, CompletionTokens&&... tokens); // defer_at: template<class Clock, class Duration, class... CompletionTokens> auto defer_at(const chrono::time_point<Clock, Duration>& abs_time, CompletionTokens&&... tokens); template<class Clock, class Duration, class Executor, class... CompletionTokens> auto defer_at(const chrono::time_point<Clock, Duration>& abs_time, const Executor& ex, CompletionTokens&&... tokens); template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens> auto defer_at(const chrono::time_point<Clock, Duration>& abs_time, ExecutionContext& ctx, CompletionTokens&&... tokens); // dispatch_after: template<class Rep, class Period, class... CompletionTokens> auto dispatch_after(const chrono::duration<Rep, Period>& rel_time, CompletionTokens&&... token); template<class Rep, class Period, class Executor, class... CompletionTokens> auto dispatch_after(const chrono::duration<Rep, Period>& rel_time, const Executor& ex, CompletionTokens&&... tokens); template<class Rep, class Period, class ExecutionContext, class... CompletionTokens> auto dispatch_after(const chrono::duration<Rep, Period>& rel_time, ExecutionContext& ctx, CompletionTokens&&... tokens); // post_after: template<class Rep, class Period, class... CompletionTokens> auto post_after(const chrono::duration<Rep, Period>& rel_time, CompletionTokens&&... token); template<class Rep, class Period, class Executor, class... CompletionTokens> auto post_after(const chrono::duration<Rep, Period>& rel_time, const Executor& ex, CompletionTokens&&... tokens); template<class Rep, class Period, class ExecutionContext, class... CompletionTokens> auto post_after(const chrono::duration<Rep, Period>& rel_time, ExecutionContext& ctx, CompletionTokens&&... tokens); // defer_after: template<class Rep, class Period, class... CompletionTokens> auto defer_after(const chrono::duration<Rep, Period>& rel_time, CompletionTokens&&... token); template<class Rep, class Period, class Executor, class... CompletionTokens> auto defer_after(const chrono::duration<Rep, Period>& rel_time, const Executor& ex, CompletionTokens&&... tokens); template<class Rep, class Period, class ExecutionContext, class... CompletionTokens> auto defer_after(const chrono::duration<Rep, Period>& rel_time, ExecutionContext& ctx, CompletionTokens&&... tokens); } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
template<class Clock, class Duration, class... CompletionTokens> auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time, CompletionTokens&&... tokens);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectexby performingget_associated_executor(handler).
— Creates a work objectwby performingmake_work(ex).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Without blocking the current thread of execution, on expiration of the absolute timeout specified byabs_timeperformsex.dispatch(std::move(handler), alloc)followed byw.reset().
Returns:
result.get().
template<class Clock, class Duration, class Executor, class... CompletionTokens> auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time, const Executor& ex, CompletionTokens&&... tokens);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Creates a work objectwby performingmake_work(ex).
— Obtains the handler's associated executor objectex1by performingget_associated_executor(handler).
— Creates a work objectw1by performingmake_work(ex1).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Constructs a function objectfwith a function call operator that performsex1.dispatch(std::move(handler), alloc)followed byw1.reset().
— Without blocking the current thread of execution, on expiration of the absolute timeout specified byabs_timeperformsex.dispatch(std::move(f), alloc)followed byw.reset().
Returns:
result.get().
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens> auto dispatch_at(const chrono::time_point<Clock, Duration>& abs_time, ExecutionContext& ctx, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::dispatch_at(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class Clock, class Duration, class... CompletionTokens> auto post_at(const chrono::time_point<Clock, Duration>& abs_time, CompletionTokens&&... tokens);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectexby performingget_associated_executor(handler).
— Creates a work objectwby performingmake_work(ex).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Without blocking the current thread of execution, on expiration of the absolute timeout specified byabs_timeperformsex.post(std::move(handler), alloc)followed byw.reset().
Returns:
result.get().
template<class Clock, class Duration, class Executor, class... CompletionTokens> auto post_at(const chrono::time_point<Clock, Duration>& abs_time, const Executor& ex, CompletionTokens&&... tokens);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Creates a work objectwby performingmake_work(ex).
— Obtains the handler's associated executor objectex1by performingget_associated_executor(handler).
— Creates a work objectw1by performingmake_work(ex1).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Constructs a function objectfwith a function call operator that performsex1.dispatch(std::move(handler), alloc)followed byw1.reset().
— Without blocking the current thread of execution, on expiration of the absolute timeout specified byabs_timeperformsex.post(std::move(f), alloc)followed byw.reset().
Returns:
result.get().
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens> auto post_at(const chrono::time_point<Clock, Duration>& abs_time, ExecutionContext& ctx, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::post_at(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class Clock, class Duration, class... CompletionTokens> auto defer_at(const chrono::time_point<Clock, Duration>& abs_time, CompletionTokens&&... tokens);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Obtains the handler's associated executor objectexby performingget_associated_executor(handler).
— Creates a work objectwby performingmake_work(ex).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Without blocking the current thread of execution, on expiration of the absolute timeout specified byabs_timeperformsex.defer(std::move(handler), alloc)followed byw.reset().
Returns:
result.get().
template<class Clock, class Duration, class Executor, class... CompletionTokens> auto defer_at(const chrono::time_point<Clock, Duration>& abs_time, const Executor& ex, CompletionTokens&&... tokens);
Let the type
Handlerbe the handler function object type determined by performinghandler_type_t<CompletionToken, void()>.
Requires: The type
Handlermust satisfy theMoveConstructiblerequirements (C++ Std, [moveconstructible]) and be callable with zero arguments.
Effects:
— Constructs a function objecthandlerof typeHandler, initialized withhandler(forward<CompletionToken>(token)).
— Constructs an objectresultof typeasync_result<Handler>, initializing the object asresult(handler).
— Creates a work objectwby performingmake_work(ex).
— Obtains the handler's associated executor objectex1by performingget_associated_executor(handler).
— Creates a work objectw1by performingmake_work(ex1).
— Obtains the handler's associated allocator objectallocby performingget_associated_allocator(handler).
— Constructs a function objectfwith a function call operator that performsex1.dispatch(std::move(handler), alloc)followed byw1.reset().
— Without blocking the current thread of execution, on expiration of the absolute timeout specified byabs_timeperformsex.defer(std::move(f), alloc)followed byw.reset().
Returns:
result.get().
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Clock, class Duration, class ExecutionContext, class... CompletionTokens> auto defer_at(const chrono::time_point<Clock, Duration>& abs_time, ExecutionContext& ctx, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::defer_at(ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class Rep, class Period, class... CompletionTokens> auto dispatch_after(const chrono::duration<Rep, Period>& rel_time, CompletionTokens&&... token);
Returns:
std::experimental::concurrency_v1::dispatch_at(chrono::steady_clock::now() + rel_time, forward<CompletionToken>(token)).
template<class Rep, class Period, class Executor, class... CompletionTokens> auto dispatch_after(const chrono::duration<Rep, Period>& rel_time, const Executor& e, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::dispatch_at(chrono::steady_clock::now() + rel_time, e, forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Rep, class Period, class ExecutionContext, class... CompletionTokens> auto dispatch_after(const chrono::duration<Rep, Period>& rel_time, ExecutionContext& c, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::dispatch_at(chrono::steady_clock::now() + rel_time, ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class Rep, class Period, class... CompletionTokens> auto post_after(const chrono::duration<Rep, Period>& rel_time, CompletionTokens&&... token);
Returns:
std::experimental::concurrency_v1::post_at(chrono::steady_clock::now() + rel_time, forward<CompletionToken>(token)).
template<class Rep, class Period, class Executor, class... CompletionTokens> auto post_after(const chrono::duration<Rep, Period>& rel_time, const Executor& e, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::post_at(chrono::steady_clock::now() + rel_time, e, forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Rep, class Period, class ExecutionContext, class... CompletionTokens> auto post_after(const chrono::duration<Rep, Period>& rel_time, ExecutionContext& c, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::post_at(chrono::steady_clock::now() + rel_time, ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
template<class Rep, class Period, class... CompletionTokens> auto defer_after(const chrono::duration<Rep, Period>& rel_time, CompletionTokens&&... token);
Returns:
std::experimental::concurrency_v1::defer_at(chrono::steady_clock::now() + rel_time, forward<CompletionToken>(token)).
template<class Rep, class Period, class Executor, class... CompletionTokens> auto defer_after(const chrono::duration<Rep, Period>& rel_time, const Executor& e, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::defer_at(chrono::steady_clock::now() + rel_time, e, forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_executor<Executor>::valueis true.
template<class Rep, class Period, class ExecutionContext, class... CompletionTokens> auto defer_after(const chrono::duration<Rep, Period>& rel_time, ExecutionContext& c, CompletionTokens&&... tokens);
Returns:
std::experimental::concurrency_v1::defer_at(chrono::steady_clock::now() + rel_time, ctx.get_executor(), forward<CompletionToken>(token)).
Remarks: This function shall not participate in overload resolution unless
is_convertible<ExecutionContext&, execution_context&>::valueis true.
#include <future> namespace std { namespace experimental { inline namespace concurrency_v1 { template <class R, class... Args> class async_result<packaged_task<R(Args...)>>; template <class Signature, class Alloc> class packaged_handler; template <class Signature, class Alloc> class async_result<packaged_handler<Signature, Alloc>>; template <class Func, class Alloc = allocator<void>> class packaged_token; template <class Func, class Alloc, class R, class... Args> struct handler_type<packaged_token<Func, Alloc>, R(Args...)>; template <class Func, class Alloc = allocator<void>> packaged_token<decay_t<Func>, Alloc> package(Func&& f, const Alloc& a = Alloc()); } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace concurrency_v1 { template <class R, class... Args> class async_result<packaged_task<R(Args...)>> { public: typedef future<R> type; async_result(packaged_task<R(Args...)>& t); async_result(const async_result&) = delete; async_result& operator=(const async_result&) = delete; type get(); private: type future_; // exposition only }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
The implementation shall provide a specialization of async_result
that meets the async_result
specialization requirements.
async_result(packaged_task<R(Args...)>& t);
Effects: Initializes
future_witht.get_future().
type get();
Returns:
std::move(future_).
namespace std { namespace experimental { inline namespace concurrency_v1 { template <class Signature, class Alloc> class packaged_handler : public packaged_task<Signature> { public: // packaged_handler types: typedef Alloc allocator_type; // packaged_handler constructors: template <class Func> explicit packaged_handler(packaged_token<Func, Alloc>&& token); // packaged_handler operations: allocator_type get_allocator() const noexcept; private: Alloc allocator_; // exposition only }; template <class Signature, class Alloc> class async_result<packaged_handler<Signature, Alloc>>; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
template <class Func> explicit packaged_handler(packaged_token<Func, Alloc>&& token);
Effects: Constructs an object of class
packaged_handler<Signature, Alloc>, initializing the base class withpackaged_task<Signature>(std::move(token.f_))and initializingallocator_withtoken.allocator_.
allocator_type get_allocator() const noexcept;
Returns:
allocator_.
namespace std { namespace experimental { inline namespace concurrency_v1 { template <class Signature, class Alloc> class async_result<packaged_handler<Signature, Alloc>> : public async_result<packaged_task<Signature>> { public: explicit async_result(packaged_handler<Signature, Alloc>& h); }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
The implementation shall provide a specialization of async_result
that meets the async_result
specialization requirements.
explicit async_result(packaged_handler<Signature, Alloc>& h);
Effects: Initializes the base class with
h.
namespace std { namespace experimental { inline namespace concurrency_v1 { template <class Func, class Alloc = allocator<void>> class packaged_token { public: // packaged_token types: typedef Alloc allocator_type; // packaged_token constructors: explicit packaged_token(Func f); packaged_token(Func f, const Alloc& a); // packaged_token operations: allocator_type get_allocator() const noexcept; private: Func f_; // exposition only Alloc allocator_; // exposition only }; template <class Func, class Alloc, class R, class... Args> struct handler_type<packaged_token<Func, Alloc>, R(Args...)> { typedef packaged_handler<result_of_t<Func(Args...)>(Args...), Alloc> type; }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
explicit packaged_token(Func f);
Effects: Constructs an object of class
packaged_token<Func, Alloc>, initializingf_withstd::move(f)and default constructingallocator_.
packaged_token(Func f, const Alloc& a);
Effects: Constructs an object of class
packaged_token<Func, Alloc>, initializingf_withstd::move(f)andallocator_witha.
template <class Func, class Alloc = allocator<void>> packaged_token<decay_t<Func>, Alloc> package(Func&& f, const Alloc& a = Alloc());
Returns:
packaged_token<decay_t<Func>, Alloc>(forward<Func>(f), a).
namespace std { namespace experimental { inline namespace concurrency_v1 { class thread_pool; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace concurrency_v1 { class thread_pool : public execution_context { public: // types: class executor_type; // construct / copy / destroy: thread_pool(); explicit thread_pool(std::size_t num_threads); thread_pool(const thread_pool&) = delete; thread_pool& operator=(const thread_pool&) = delete; ~thread_pool(); // thread_pool operations: executor_type get_executor() const noexcept; void stop(); void join(); }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
Class thread_pool implements
a fixed-size pool of threads.
An object of type thread_pool
has an associated executor object, meeting the Executor
type requirements, of type thread_pool::executor_type
and obtainable via the thread_pool
object's get_executor member
function.
For an object of type thread_pool,
outstanding work is defined as the sum of:
— the total number of calls to the thread_pool
executor's on_work_started
function, less the total number of calls to the on_work_finished
function;
— the number of function objects that have been added to the thread_pool via the thread_pool
executor, but not yet executed; and
— the number of function objects that are currently being executed by the
thread_pool.
The thread_pool member functions
get_executor, stop, and join,
and the thread_pool::executor_type copy constructors, member
functions and comparison operators, shall not introduce data races as a result
of concurrent calls to those functions from different threads.
thread_pool(); explicit thread_pool(std::size_t num_threads);
Effects: Creates an object of class
thread_poolcontaining a number of threads of execution, each represented by athreadobject. If specified, the number of threads in the pool isnum_threads. Otherwise, the number of threads in the pool is implementation-defined. [Note: A suggested value for the implementation-defined number of threads isstd::thread::hardware_concurrency() * 2. —end note]
~thread_pool();
Effects: Destroys an object of class
thread_pool. Performsstop()followed byjoin().
executor_type get_executor() const noexcept;
Returns: An executor that may be used for submitting function objects to the
thread_pool.
void stop();
Effects: Signals the threads in the pool to complete as soon as possible. If a thread is currently executing a function object, the thread will exit only after completion of that function object. The call to
stop()returns without waiting for the threads to complete.
void join();
Effects: If not already stopped, signals the threads in the pool to exit once the outstanding work is
0. Blocks until all threads in the pool have completed.
Synchronization: The completion of each thread in the pool synchronizes with (C++ Std, [intro.multithread]) the corresponding successful
join()return.
Postconditions: The threads in the pool represented by
*thishave completed.
namespace std { namespace experimental { inline namespace concurrency_v1 { class thread_pool::executor_type { public: // construct / copy / destroy: executor_type(const executor_type& other) noexcept; executor_type(executor_type&& other) noexcept; executor_type& operator=(const executor_type& other) noexcept; executor_type& operator=(executor_type&& other) noexcept; ~executor_type(); // executor operations: bool running_in_this_thread() const noexcept; thread_pool& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a); template<class Func, class Alloc> void post(Func&& f, const Alloc& a); template<class Func, class Alloc> void defer(Func&& f, const Alloc& a); }; bool operator==(const thread_pool::executor_type& a, const thread_pool::executor_type& b) noexcept; bool operator!=(const thread_pool::executor_type& a, const thread_pool::executor_type& b) noexcept; template<> struct is_executor<thread_pool::executor_type> : true_type {}; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
thread_pool::executor_type is a type satisfying Executor requirements. Objects of
type thread_pool::executor_type are associated with a thread_pool, and function objects submitted
using the dispatch, post or defer
member functions will be executed by the thread_pool.
executor_type(const executor_type& other) noexcept;
Effects: Constructs an object of class
thread_pool::executor_type.
Postconditions:
*this == other.
executor_type(executor_type&& other) noexcept;
Effects: Constructs an object of class
thread_pool::executor_type.
Postconditions:
*thisis equal to the prior value ofother.
executor_type& operator=(const executor_type& other) noexcept;
Postconditions:
*this == other.
Returns:
*this.
executor_type& operator=(executor_type&& other) noexcept;
Postconditions:
*thisis equal to the prior value ofother.
Returns:
*this.
bool running_in_this_thread() const noexcept;
Returns:
trueif the current thread of execution is a member of the pool; otherwisefalse.
thread_pool& context() noexcept;
Returns: A reference to the associated
thread_poolobject.
void on_work_started() noexcept;
Effects: Increases the count of outstanding work associated with the
thread_pool.
void on_work_finished() noexcept;
Effects: Decreases the count of outstanding work associated with the
thread_pool.
template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a);
Effects: If
running_in_this_thread()is true, callsDECAY_COPY(forward<Func>(f))(). Otherwise, requests execution off.
If
fexits via an exception, and the execution offis performed in the current thread and beforedispatchreturns, the exception shall propagate to the caller ofdispatch.
template<class Func, class Alloc> void post(Func&& f, const Alloc& a);
Effects: Requests execution of
f.
template<class Func, class Alloc> void defer(Func&& f, const Alloc& a);
Effects: Requests execution of
f.
bool operator==(const thread_pool::executor_type& a, const thread_pool::executor_type& b) noexcept;
Returns:
&a.context() == &b.context().
bool operator!=(const thread_pool::executor_type& a, const thread_pool::executor_type& b) noexcept;
Returns:
!(a == b).
namespace std { namespace experimental { inline namespace concurrency_v1 { class loop_scheduler; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
namespace std { namespace experimental { inline namespace concurrency_v1 { class loop_scheduler : public execution_context { public: // types: class executor_type; // construct / copy / destroy: loop_scheduler(); explicit loop_scheduler(std::size_t concurrency_hint); loop_scheduler(const loop_scheduler&) = delete; loop_scheduler& operator=(const loop_scheduler&) = delete; ~loop_scheduler(); // loop_scheduler operations: executor_type get_executor() const noexcept; size_t run(); template<class Rep, class Period> size_t run_for(const chrono::duration<Rep, Period>& rel_time); template<class Clock, class Duration> size_t run_until(const chrono::time_point<Clock, Duration>& abs_time); size_t run_one(); template<class Rep, class Period> size_t run_one_for(const chrono::duration<Rep, Period>& rel_time); template<class Clock, class Duration> size_t run_one_until(const chrono::time_point<Clock, Duration>& abs_time); size_t poll(); size_t poll_one(); void stop(); bool stopped() const; void restart(); }; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
An object of type loop_scheduler
has an associated executor object, meeting the Executor
type requirements, of type loop_scheduler::executor_type
and obtainable via the loop_scheduler
object's get_executor member
function.
For an object of type loop_scheduler,
outstanding work is defined as the sum of:
— the total number of calls to the loop_scheduler
executor's on_work_started
function, less the total number of calls to the on_work_finished
function;
— the number of function objects that have been added to the loop_scheduler via the loop_scheduler
executor, but not yet executed; and
— the number of function objects that are currently being executed by the
loop_scheduler.
If at any time the outstanding work falls to 0,
the loop_scheduler is stopped
as if by stop().
The loop_scheduler member
functions get_executor,
run, run_for,
run_until, run_one, run_one_for,
run_one_until, poll, poll_one,
stop, and stopped,
and the loop_scheduler::executor_type copy constructors, member
functions and comparison operators, shall not introduce data races as a result
of concurrent calls to those functions from different threads. [Note:
The restart member function
is excluded from these thread safety requirements. —end note]
loop_scheduler(); explicit loop_scheduler(std::size_t concurrency_hint);
Effects: Creates an object of class
loop_scheduler.
Remarks: The
concurrency_hintparameter is a suggestion to the implementation on the number of threads that should process asynchronous operations and execute function objects.
~loop_scheduler();
Effects: Destroys an object of class
loop_scheduler.
executor_type get_executor() const noexcept;
Returns: An executor that may be used for submitting function objects to the
loop_scheduler.
size_t run();
Requires: Must not be called from a thread that is currently calling one of
run,run_for,run_until,run_one,run_one_for,run_one_until,poll, orpoll_one.
Effects: Equivalent to:
size_t n = 0; while (run_one()) if (n != numeric_limits<size_t>::max()) ++n;
Returns:
n.
template<class Rep, class Period> size_t run_for(const chrono::duration<Rep, Period>& rel_time);
Effects: Equivalent to:
return run_until(chrono::steady_clock::now() + rel_time);
template<class Clock, class Duration> size_t run_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: Equivalent to:
size_t n = 0; while (run_one_until(abs_time)) if (n != numeric_limits<size_t>::max()) ++n;
Returns:
n.
size_t run_one();
Requires: Must not be called from a thread that is currently calling one of
run,run_for,run_until,run_one,run_one_for,run_one_until,poll, orpoll_one.
Effects: If the
loop_schedulerobject has no oustanding work, performsstop(). Otherwise, blocks while the loop_scheduler has outstanding work, or until theloop_scheduleris stopped, or until one function object has been executed.
If an executed function object throws an exception, the exception shall be allowed to propagate to the caller of
run_one(). Theloop_schedulerstate shall be as if the function object had returned normally.
Returns:
1if a function object was executed, otherwise0.
Notes: This function may invoke additional handlers through nested calls to the
loop_schedulerexecutor'sdispatchmember function. These do not count towards the return value.
template<class Rep, class Period> size_t run_one_for(const chrono::duration<Rep, Period>& rel_time);
Effects: Equivalent to:
return run_until(chrono::steady_clock::now() + rel_time);
Returns:
1if a function object was executed, otherwise0.
template<class Clock, class Duration> size_t run_one_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: If the
loop_schedulerobject has no oustanding work, performsstop(). Otherwise, blocks while theloop_schedulerhas outstanding work, or until the expiration of the absolute timeout (C++ Std, [thread.req.timing]) specified byabs_time, or until theloop_scheduleris stopped, or until one function object has been executed.
If an executed function object throws an exception, the exception shall be allowed to propagate to the caller of
run_one(). Theloop_schedulerstate shall be as if the function object had returned normally.
Returns:
1if a function object was executed, otherwise0.
Notes: This function may invoke additional handlers through nested calls to the
loop_schedulerexecutor'sdispatchmember function. These do not count towards the return value.
size_t poll();
Effects: Equivalent to:
size_t n = 0; while (poll_one()) if (n != numeric_limits<size_t>::max()) ++n;
Returns:
n.
size_t poll_one();
Effects: If the
loop_schedulerobject has no oustanding work, performsstop(). Otherwise, if there is a function object ready for immediate execution, executes it.
If an executed function object throws an exception, the exception shall be allowed to propagate to the caller of
poll_one(). Theloop_schedulerstate shall be as if the function object had returned normally.
Returns:
1if a handler was executed, otherwise0.
Notes: This function may invoke additional handlers through nested calls to the
loop_schedulerexecutor'sdispatchmember function. These do not count towards the return value.
void stop();
Effects: Stops the
loop_scheduler. Concurrent calls torun,run_for,run_until,run_one,run_one_for,run_one_until,pollorpoll_onewill end as soon as possible. If a call torun,run_for,run_until,run_one,run_one_for,run_one_until,pollorpoll_oneis currently executing a function object, the call will end only after completion of that function object. The call tostop()returns without waiting for concurrent calls torun,run_for,run_until,run_one,run_one_for,run_one_until,pollorpoll_oneto complete.
Postconditions:
stopped() == true.
[Note: When
stopped() == true, subsequent calls torun,run_for,run_until,run_one,run_one_for,run_one_until,pollorpoll_onewill exit immediately with a return value of0, without executing any function objects. Aloop_schedulerremains in the stopped state until a call torestart(). —end note]
void restart();
Postconditions:
stopped() == false.
namespace std { namespace experimental { inline namespace concurrency_v1 { class loop_scheduler::executor_type { public: // construct / copy / destroy: executor_type(const executor_type& other) noexcept; executor_type(executor_type&& other) noexcept; executor_type& operator=(const executor_type& other) noexcept; executor_type& operator=(executor_type&& other) noexcept; ~executor_type(); // executor operations: bool running_in_this_thread() const noexcept; loop_scheduler& context() noexcept; void on_work_started() noexcept; void on_work_finished() noexcept; template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a); template<class Func, class Alloc> void post(Func&& f, const Alloc& a); template<class Func, class Alloc> void defer(Func&& f, const Alloc& a); }; bool operator==(const loop_scheduler::executor_type& a, const loop_scheduler::executor_type& b) noexcept; bool operator!=(const loop_scheduler::executor_type& a, const loop_scheduler::executor_type& b) noexcept; template<> struct is_executor<loop_scheduler::executor_type> : true_type {}; } // inline namespace concurrency_v1 } // namespace experimental } // namespace std
loop_scheduler::executor_type is a type satisfying Executor requirements. Objects of
type loop_scheduler::executor_type are associated with a loop_scheduler, and function objects submitted
using the dispatch, post or defer
member functions will be executed by the loop_scheduler
from within the run, run_for, run_until,
run_one, run_one_for,
run_one_until, poll or poll_one
functions.
executor_type(const executor_type& other) noexcept;
Effects: Constructs an object of class
loop_scheduler::executor_type.
Postconditions:
*this == other.
executor_type(executor_type&& other) noexcept;
Effects: Constructs an object of class
loop_scheduler::executor_type.
Postconditions:
*thisis equal to the prior value ofother.
executor_type& operator=(const executor_type& other) noexcept;
Postconditions:
*this == other.
Returns:
*this.
executor_type& operator=(executor_type&& other) noexcept;
Postconditions:
*thisis equal to the prior value ofother.
Returns:
*this.
bool running_in_this_thread() const noexcept;
Returns:
trueif the current thread of execution is invoking therun,run_for,run_until,run_one,run_one_for,run_one_until,pollorpoll_onefunction of the associatedloop_schedulerobject.
loop_scheduler& context() noexcept;
Returns: A reference to the associated
loop_schedulerobject.
void on_work_started() noexcept;
Effects: Increases the count of outstanding work associated with the
loop_scheduler.
void on_work_finished() noexcept;
Effects: Decreases the count of outstanding work associated with the
loop_scheduler.
template<class Func, class Alloc> void dispatch(Func&& f, const Alloc& a);
Effects: If
running_in_this_thread()is true, callsDECAY_COPY(forward<Func>(f))(). Otherwise, requests execution off.
If
fexits via an exception, and the execution offis performed in the current thread and beforedispatchreturns, the exception shall propagate to the caller ofdispatch.
template<class Func, class Alloc> void post(Func&& f, const Alloc& a);
Effects: Requests execution of
f.
template<class Func, class Alloc> void defer(Func&& f, const Alloc& a);
Effects: Requests execution of
f.
bool operator==(const loop_scheduler::executor_type& a, const loop_scheduler::executor_type& b) noexcept;
Returns:
&a.context() == &b.context().
bool operator!=(const loop_scheduler::executor_type& a, const loop_scheduler::executor_type& b) noexcept;
Returns:
!(a == b).