General

Scope

This technical specification describes a number of concurrency extensions to
the C++ Standard Library (1.2). These
extensions are classes and functions that are likely to be used widely within a
program and/or on the interface boundaries between libraries written by
different organizations.

This technical specification is non-normative. Some of the library components
in this technical specification may be considered for standardization in a
future version of C++, but they are not currently part of any C++ standard. Some
of the components in this technical specification may never be standardized, and
others may be standardized in a substantially changed form.

The goal of this technical `specification` is to build more
widespread existing practice for an expanded C++ standard
library. It gives advice on extensions to those vendors who wish
to provide them.

1.2

Normative references

The following referenced document is indispensable for the
application of this document. For dated references, only the
edition cited applies. For undated references, the latest edition
of the referenced document (including any amendments) applies.

ISO/IEC 14882:— is herein called the C++ Standard.
References to clauses within the C++ Standard are written as "C++14
§3.2". The library described in ISO/IEC 14882:— clauses 17–30 is
herein called the C++ Standard Library.

1.3

Namespaces, headers, and modifications to standard classes

Some of the extensions described in this Technical Specification represent
types and functions that are currently not part of the C++ Standards Library,
and because these extensions are experimental, they should not be declared
directly within namespace std. Instead, such extensions are
declared in namespace std::experimental.

[ Note:
Once standardized, these components are expected to be promoted to namespace std.
— end note ]

Unless otherwise specified, references to such entities described in this
Technical Specification are assumed to be qualified with
std::experimental, and references to entities described in the C++
Standard Library are assumed to be qualified with std::.

The threetwo functions differ only by input parameters. The first only
takes a callable object which accepts a future object as a parameter. The
second function takes an executor as the first parameter and a callable object
as the second parameter. The thirdsecond function takes a launch policy as the first
parameter and a callable object as the second parameter.

Effects:

The continuationINVOKE(DECAY_COPY (std::forward<F>(func))) is called when the object's shared state is ready (has a value or exception stored).

The continuation launches according to the specified launch policy or
executor.

When the executor or launch policy is not provided the continuation inherits
the parent's launch policy or executor.

Any value returned from the continuation is stored as the result in the
shared state of the resulting future. Any exception propagated from the execution of
the continuation is stored as the exceptional result in the shared state of the resulting future.

If the parent was created with std::promise or with a packaged_task (has
no associated launch policy), the continuation behaves the same as the thirdsecond
overload with a policy argument of launch::async | launch::deferred and the
same argument for func.

If the parent has a policy of launch::deferred, then it is filled by
calling wait() or get() on the resulting future.
[ Example:

The return type of then depends on the return type of the closure
func as defined below:

When result_of_t<decay_t<F>()>
is future<R>, the function returns future<R>.

Otherwise, the function returns future<result_of_t<decay_t<F>()>>.

Notes:

The first rule above is called the implicit unwrapping. Without this rule,
the return type of then taking a closure returning a
future<R> would have been future<future<R>>.
This rule avoids such nested future objects.
[ Example:
The type of f2 below is
future<int> and not future<future<int>>:

The threetwo functions differ only by input parameters. The first
only takes a callable object which accepts a shared_future object as a
parameter. The second function takes an executor as the first parameter and a
callable object as the second parameter. The thirdsecond function takes a launch
policy as the first parameter and a callable object as the second parameter.

Effects:

The continuationINVOKE(DECAY_COPY (std::forward<F>(func))) is called when the object's shared state is ready (has a value or exception stored).

The continuation launches according to the specified policy or executor.

When the scheduler or launch policy is not provided the continuation
inherits the parent's launch policy or executor.

Any value returned from the continuation is stored as the result in the
shared state of the resulting future. Any exception propagated from the execution of
the continuation is stored as the exceptional result in the shared state of the resulting future.

If the parent was created with std::promise (has no associated launch
policy), the continuation behaves the same as the thirdsecond function with a policy
argument of launch::async | launch::deferred and the same argument for func.

If the parent has a policy of launch::deferred, then it is filled by
calling wait() or get() on the resulting shared_future.
[ Note:
This is similar to future. See example in 2.2.
— end note ]

Returns:

The return type of then depends on the return type of the closure
func as defined below:

When result_of_t<decay_t<F>()>
is future<R>, the function returns future<R>.

Otherwise, the function returns future<result_of_t<decay_t<F>()>>.

Notes:

This is the same as in future. See the notes on future::then return type in 2.2.

Postconditions:

The shared_future passed to the continuation function is
a copy of the original shared_future.

There are two variations of when_all. The first version takes a pair of
InputIterators. The second takes any arbitrary number of future<R0> and
shared_future<R1> objects, where R0 and R1 need not be the same type.

Calling the first signature of when_all where InputIterator first
equals last, returns a future with an empty vector that is immediately
ready.

Calling the second signature of when_any with no arguments returns a
future<tuple<>> that is immediately ready.

Effects:

Each future and shared_future is waited upon and then copied into the
collection of the output (returned) future, maintaining the order of the
futures in the input collection.

The future returned by when_all will not throw an exception, but the
futures held in the output collection may.

Returns:

future<tuple<>> if when_all is called with zero arguments.

future<vector<future<R>>> if the input cardinality is unknown at compile
and the iterator pair yields future<R>. R may be void. The order of the
futures in the output vector will be the same as given by the input iterator.

future<vector<shared_future<R>>> if the input cardinality is unknown at
compile time and the iterator pair yields shared_future<R>. R may be
void. The order of the futures in the output vector will be the same as given
by the input iterator.

future<tuple<future<R0>, future<R1>, future<R2>...>> if inputs are fixed in
number. The inputs can be any arbitrary number of future and shared_future
objects. The type of the element at each position of the tuple corresponds to
the type of the argument at the same position. Any of R0, R1, R2, etc.
may be void.

There are two variations of when_any. The first version takes a pair of
InputIterators. The second takes any arbitrary number of future<R> and
shared_future<R> objects, where R need not be the same type.

Calling the first signature of when_any where InputIterator first
equals last, returns a future with an empty vector that is immediately
ready.

Calling the second signature of when_any with no arguments returns a
future<tuple<>> that is immediately ready.

Effects:

Each future and shared_future is waited upon. When at least one is ready,
all the futures are copied into the collection of the output (returned) future,
maintaining the order of the futures in the input collection.

The future returned by when_any will not throw an exception, but the
futures held in the output collection may.

Returns:

future<tuple<>> if when_any is called with zero arguments.

future<vector<future<R>>> if the input cardinality is unknown at compile
time and the iterator pair yields future<R>. R may be void. The order of
the futures in the output vector will be the same as given by the input
iterator.

future<vector<shared_future<R>>> if the input cardinality is unknown at
compile time and the iterator pair yields shared_future<R>. R may be
void. The order of the futures in the output vector will be the same as given
by the input iterator.

future<tuple<future<R0>, future<R1>, future<R2>...>> if inputs are fixed in
number. The inputs can be any arbitrary number of future and shared_future
objects. The type of the element at each position of the tuple corresponds to
the type of the argument at the same position. Any of R0, R1, R2, etc.
maybe void.

InputIterator's value type shall be convertible to future<R>
or shared_future<R>. All R types must be the same.

Notes:

The function when_any_back takes a pair of InputIterators.

Calling when_any_back where InputIterator first equals
last, returns a future with an empty vector that is immediately ready.

Effects:

Each future and shared_future is waited upon. When at least one is ready,
all the futures are copied into the collection of the output (returned)
future.

After the copy, the future or shared_future that was first detected as
being ready swaps its position with that of the last element of the result
collection, so that the ready future or shared_future may be identified in
constant time. Only one future or shared_future is thus moved.

The future returned by when_any_back will not throw an exception, but
the futures held in the output collection may.

Returns:

future<vector<future<R>>> if the input cardinality is unknown at compile
time and the iterator pair yields future<R>. R may be void.

future<vector<shared_future<R>>> if the input cardinality is unknown at
compile time and the iterator pair yields shared_future<R>. R may be
void.

The first function behaves the same as a call to the second
function with a policy argument of launch::async | launch::deferred and the
same arguments for F and Args. The second and third functions creates a
shared state that is associated with the returned future object. The further
behavior of the second function depends on the policy argument as follows (if
more than one of these conditions applies, the implementation may choose any of
the corresponding policies):

if policy & launch::async is non-zero — calls
INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
(20.8.2, 30.3.1.2) as if in a new thread of execution represented by a thread object
with the calls to DECAY_COPY () being evaluated in the thread that called
async. Any return value is stored as the result in the shared state. Any
exception propagated from the execution of
INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
is stored as the exceptional result in the shared state. The thread object is stored in the
shared state and affects the behavior of any asynchronous return objects that
reference that state.

if policy & launch::deferred is non-zero — Stores DECAY_COPY(std::forward<F>(f))
and DECAY_COPY (std::forward<Args>(args))... in the
shared state. These copies of f and args constitute a deferred function.
Invocation of the deferred function evaluates
INVOKE std::move(g), std::move(xyz)) where g is the stored value of
DECAY_COPY (std::forward<F>(f)) and xyz is the stored copy of
DECAY_COPY (std::forward<Args>(args)).... The shared state is not made ready until the
function has completed. The first call to a non-timed waiting function (30.6.4)
on an asynchronous return object referring to this shared state shall invoke
the deferred function in the thread that called the waiting function. Once
evaluation of INVOKE (std::move(g), std::move(xyz)) begins, the function is no
longer considered deferred. [ Note:
If this policy is specified together with
other policies, such as when using a policy value of
launch::async | launch::deferred, implementations should defer invocation or the selection of
the policy when no more concurrency can be effectively exploited.
— end note ]

If no value is set in the launch policy, or a value is set that is
neither specified in this International Standard or by the implementation,
the behaviour is undefined.

The further behavior of the third function is as follows:

The executor::add() function is given a function<void()> which calls
INVOKE (DECAY_COPY (std::forward<F>(f)) DECAY_COPY
(std::forward<Args>(args))...). The implementation of the executor is decided
by the programmer.