std::future<T>
and Related APIs
The extensions proposed here are an evolution of the functionality of
std::future
and std::shared_future
. The extensions
enable wait free composition of asynchronous operations. Class templates
std::promise
, std::packaged_task
and function
template std::async
are also updated to be compatible with the
updated std::future
.
swap
for packaged_task
#include <future>
namespace std {
namespace experimental {
inline namespace concurrency_v1 {
template <class R> class promise;
template <class R> class promise<R&>;
template <> class promise<void>;
template <class R>
void swap(promise<R>& x, promise<R>& y) noexcept;
template <class R> class future;
template <class R> class future<R&>;
template <> class future<void>;
template <class R> class shared_future;
template <class R> class shared_future<R&>;
template <> class shared_future<void>;
template <class> class packaged_task; // undefined
template <class R, class... ArgTypes>
class packaged_task<R(ArgTypes...)>;
template <class R, class... ArgTypes>
void swap(packaged_task<R(ArgTypes...)>&, packaged_task<R(ArgTypes...)>&) noexcept;
template <class F, class... Args>
future<result_of_t<decay_t<F>(decay_t<Args>...)>>
async(F&& f, Args&&... args);
template <class F, class... Args>
future<result_of_t<decay_t<F>(decay_t<Args>...)>>
async(launch policy, F&& f, Args&&... args);
template <class T>
future<decay_t<T>> make_ready_future(T&& value);
future<void> make_ready_future();
future<T> make_exceptional_future(exception_ptr value);
template <class T, class E>
future<T> make_exceptional_future(E ex);
template <class InputIterator>
see below when_all(InputIterator first, InputIterator last);
template <class... Futures>
see below when_all(Futures&&... futures);
template <class InputIterator>
see below when_any(InputIterator first, InputIterator last);
template <class... Futures>
see below when_any(Futures&&... futures);
template <class InputIterator>
see below when_any_back(InputIterator first, InputIterator last);
} // namespace concurrency_v1
} // namespace experimental
template <class R, class Alloc>
struct uses_allocator<experimental::promise<R>, Alloc>;
template <class R, class Alloc>
struct uses_allocator<experimental::packaged_task<R>, Alloc>;
} // namespace std
future
The specification of all declarations within this sub-clause
namespace std {
namespace experimental {
inline namespace concurrency_v1 {
template <class R>
class future {
public:
future() noexcept;
future(future &&) noexcept;
future(const future& rhs) = delete;
future(future<future<R>>&& rhs) noexcept;
~future();
future& operator=(const future& rhs) = delete;
future& operator=(future&&) noexcept;
shared_future&<R> share();
// retrieving the value
see below get();
// functions to check state
bool valid() const noexcept;
bool is_ready() const;
void wait() const;
template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
template <class Clock, class Duration>
future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
// continuations
template<class F>
see below then(F&& func);
template<class F>
see below then(launch policy, F&& func);
};
} // namespace concurrency_v1
} // namespace experimental
} // namespace std
In
future
object from the shared state referred to by
rhs
future
future
becomes ready when one of the following occurs:
future
s are ready. The future
inherits the value or the exception from the inner future
.
future
is ready but the inner future
is invalid. The future
gets an exception of type std::future_error
, with an error code of std::future_errc::broken_promise
.
valid()
returns the same value as rhs.valid()
prior to the
constructor invocation.valid() == true
.rhs.valid() == false
.
After
The member function template then
provides a mechanism for attaching
a continuation to a future
object, that will be executed
as specified below.
INVOKE(func, *this)
shall be a valid expression.future
object as a parameter. The
second function takes a launch policy as the first
parameter and a callable object as the second parameter.
future
object. The further behavior of the functions is as follows.
INVOKE(DECAY_COPY (std::forward<F>(func)))
is called when the object's shared state is ready (has a value or exception stored).future
. Any exception propagated from the execution of
the continuation is stored as the exceptional result in the shared state of the resulting future
.
std::promise
or with a packaged_task
(has
no associated launch policy), the continuation behaves the same as in the second
overload with a policy argument of launch::async | launch::deferred
and the
same argument for func
.launch::deferred
, then it is filled by
calling wait()
or get()
on the resulting future
.
auto f1 = async(launch::deferred, [] { return 1; }); auto f2 = f1.then([](future<int> n) { return 2; }); f2.wait(); // execution of f1 starts here, followed by f2
then
depends on the return type of the closure
func
as defined below:
result_of_t<decay_t<F>(future<R>)>
is future<R2>
, the function returns future<R2>
.
future<result_of_t<decay_t<F>(future<R>)>>
.
then
taking a closure returning a
future<R>
would have been future<future<R>>
.
This rule avoids such nested future
objects.
f2
below is
future<int>
and not future<future<int>>
:
future<int> f1 = g();
future<int> f2 = f1.then([](future<int> f) {
future<int> f3 = h();
return f3;
});
std::async
(see future
object returned from then
will not block.
future
object is moved to the parameter of the continuation function.valid() == false
on the original future
;
valid() == true
on the future
returned from then.
future
returned from
then
cannot be established until after the completion of the
continuation. If it is not valid, the resulting future
becomes ready with an exception of type std::future_error
,
with an error code of std::future_errc::broken_promise
.
true
if the shared state is ready, false
if it isn't.shared_future
The specification of all declarations within this sub-clause
bool is_ready() const; template<typename F> see below then(F&& func); template<typename F> see below then(launch policy, F&& func);
namespace std {
namespace experimental {
inline namespace concurrency_v1 {
template <class R>
class shared_future {
public:
shared_future() noexcept;
shared_future(const shared_future &) noexcept;
shared_future(future &&) noexcept;
shared_future(shared_future &&) noexcept;
~shared_future();
shared_future& operator=(const shared_future& rhs);
shared_future& operator=(shared_future&&) noexcept;
// retrieving the value
see below get();
// functions to check state
bool valid() const noexcept;
bool is_ready() const;
void wait() const;
template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
template <class Clock, class Duration>
future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
// continuations
template<class F>
see below then(F&& func) const;
template<class F>
see below then(launch policy, F&& func) const;
};
} // namespace concurrency_v1
} // namespace experimental
} // namespace std
After
shared_future
object from the shared state referred to by
rhs
.
The shared_future
becomes ready when one of the following occurs:
future
and the inner shared_future
are ready.
The shared_future
inherits the value or the exception from the inner shared_future
.
future
is ready but the inner shared_future
is invalid.
The shared_future
gets an exception of type std::future_error
, with an error code of std::future_errc::broken_promise
.
valid()
returns the same value as rhs.valid()
prior to the
constructor invocation.valid() == true
.rhs.valid() == false
.
After
The member function template then
provides a mechanism for attaching
a continuation to a shared_future
object, that will be executed
as specified below.
INVOKE(func, *this)
shall be a valid expression.shared_future
object as a
parameter. The second function takes a launch
policy as the first parameter and a callable object which accepts a shared_future
object as a
parameter, as the second parameter.
future
object. The further behavior of the functions is as follows.
INVOKE(DECAY_COPY (std::forward<F>(func)), *this)
is called when the object's shared state is ready (has a value or exception stored).future
. Any exception propagated from the execution of
the continuation is stored as the exceptional result in the shared state of the resulting future
.
std::promise
(has no associated launch
policy), the continuation behaves the same as in the second function with a policy
argument of launch::async | launch::deferred
and the same argument for func
.launch::deferred
, then it is filled by
calling wait()
or get()
on the resulting shared_future
.
future
. See example in then
depends on the return type of the closure
func
as defined below:
result_of_t<decay_t<F>(shared_future<R>)>
is future<R2>
, the function returns future<R2>
.
future<result_of_t<decay_t<F>(shared_future<R>)>>
.
future
. See the notes on future::then
return type in
std::async
(see future
object returned from then
will not block.
shared_future
passed to the continuation function is
a copy of the original shared_future
.valid() == true
on the original shared_future
object.
valid() == true
on the shared_future
returned from then.
future
returned from
then
cannot be established until after the completion of the
continuation. In such case, the resulting future
becomes ready with an exception of type std::future_error
,
with an error code of std::future_errc::broken_promise
.
true
if the shared state is ready, false
if it isn't.true
, all subsequent invocations
on the same shared_future
object will also return true
.promise
The specification of all declarations within this sub-clause
The future
returned by the function get_future
is the one defined in the experimental
namespace (
packaged_task
The specification of all declarations within this sub-clause
The future
returned by the function get_future
is the one defined in the experimental
namespace (
async
The specification of all declarations within this sub-clause
The future
returned by the function async
is the one defined in the experimental
namespace (
when_all
A new section 30.6.10 shall be inserted at the end of
The function template when_all
creates a future
object that
becomes ready when all elements in a set of future
and shared_future
objects
become ready.
iterator_traits<InputIterator>::value_type
shall be convertible to future<R>
or shared_future<R>
, but not both.
If any of the future<R>
or shared_future<R>
objects are
in invalid state (i.e. valid() == false
), the behavior is undefined.
T
is of type future<R>
or shared_future<R>
Fi
be the ith type in Futures
,
Ui
be remove_reference_t<Fi>
, and
fi
be the ith parameter in the function parameter
pack futures
, where all indexing is zero-based. Then each Ui
shall be the type future<Ri>
or (possibly const
)
shared_future<Ri>
; and fi.valid()
shall be true
for all i.
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.when_all
where InputIterator
first
equals last, returns a future
with an empty vector that is immediately
ready.when_allwhen_any
with no arguments returns a
future<tuple<>>
that is immediately ready.future
and shared_future
is waited upon and then copied into the
collection of the output (returned) future
, maintaining the order of the
future
s in the input collection.
future
s or shared_future
s supplied
to a call to when_all
refer to deferred tasks
that have not started execution, those tasks are executed before the call
to when_all
returns.
when_all
does not wait for non-deferred tasks, or deferred tasks
that have already started executing elsewhere, to complete before returning.
future
s and shared_future
s supplied
to the call to when_all
are ready, the future
s
are moved, and the shared_future
s are copied,
into, correspondingly, future
s or shared_future
s
of a new collection, which can be either tuple
or a vector
as described below. The order of the objects in the collection matches the order
of the arguments supplied to when_all
.
future
object that refers to the shared state
is created. The exact type of the future
is further described below.
future
returned by when_all
will not throw an exception, but the
future
s and shared_future
s held in the output collection may.valid() == true
.future<T>
s valid() == false
.shared_future<T>
valid() == true
.future
s/shared_future
s are ready.
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
future
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 future
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
.when_any
A new section 30.6.11 shall be inserted at the end of
The function template when_any
creates a future
object that
becomes ready when at least one element in a set of future
and shared_future
objects
becomes ready.
iterator_traits<InputIterator>::value_type
shall be convertible to future<R>
or shared_future<R>
, but not both.
If any of the future<R>
or shared_future<R>
objects are
in invalid state (i.e. valid() == false
), the behavior is undefined.
T
is of type future<R>
or shared_future<R>
Fi
be the ith type in Futures
,
Ui
be remove_reference_t<Fi>
, and
fi
be the ith parameter in the function parameter
pack futures
, where all indexing is zero-based. Then each Ui
shall be the type future<Ri>
or (possibly const
)
shared_future<Ri>
; and fi.valid()
shall be true
for all i.
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.when_any
where InputIterator
first
equals last
, returns a future
with an empty vector that is immediately
ready.when_any
with no arguments returns a
future<tuple<>>
that is immediately ready.future
and shared_future
is waited upon. When at least one is ready,
all the future
s are copied into the collection of the output (returned) future
,
maintaining the order of the future
s in the input collection.future
s and shared_future
s supplied to
when_any
is checked in the order supplied.
If a given future
or
shared_future
refers to a deferred task that has not yet started execution,
then no further future
s or shared_future
s are checked,
and that task is executed.
when_any
does not wait for non-deferred tasks, or deferred tasks
that have already started executing elsewhere, to complete before returning.
future
s or shared_future
s
supplied to the call to when_any
are ready, the future
s
are moved, and the shared_future
s are copied
into, correspondingly, future
s or shared_future
s
of a new collection, which can be either tuple
or a vector
as described below.
when_any
.
future
object that refers to the shared state
is created. The exact type of the future
is further described below.
future
returned by when_any
will not throw
an exception, but the future
s and shared_future
s
held in the output collection may.
valid() == true
.future<T>
s valid() == false
.shared_future<T>
valid() == true
.future
s/shared_future
s are ready.
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 future
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 future
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
.when_any_back
A new section 30.6.12 shall be inserted at the end of
The function template when_any_back
creates a future
object that
becomes ready when at least one element in a set of future
and shared_future
objects
becomes ready. The ready future
or shared_future
may be identified in
constant time.
iterator_traits<InputIterator>::value_type
shall be convertible to future<R>
or shared_future<R>
.
If any of the future<R>
or shared_future<R>
objects are
in invalid state (i.e. valid() == false
), the behavior is undefined.
future
and shared_future
is waited upon. When at least one is ready,
all the future
are copied into the collection of the output (returned)
future
.future
s and shared_future
s supplied to
when_any_back
is checked in the order supplied.
If a given future
or
shared_future
refers to a deferred task that has not yet started execution,
then no further future
s or shared_future
s are checked,
and that task is executed.
when_any_back
does not wait for non-deferred tasks, or deferred tasks
that have already started executing elsewhere, to complete before returning.
future
s or shared_future
s
supplied to the call to when_any_back
are ready, the future
s
are moved, and the shared_future
s are copied
into, correspondingly, future
s or shared_future
s
of a new collection, which can be either tuple
or a vector
as described below.
when_any_back
is non-empty (i.e.,
first != last
),
the last future
or shared_future
in the output collection
is guaranteed to be in the ready state. The order of other elements in the
collection is unspecified.
future
object that refers to the newly created shared state
is created. The exact type of the future
is further described below.
future
returned by when_any_back
will not throw
an exception, but the future
s and shared_future
s
held in the output collection may.
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.valid() == true
.future<T>
s valid() == false
.shared_future<T>
valid() == true
.future
s/shared_future
s are ready.
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
.make_ready_future
A new section 30.6.13 shall be inserted at the end of
future
if it
is an rvalue. Otherwise the value is copied to the shared state of the returned future
.
future<decay_t<T>>
, if function is given a value of type T
.future<void>
, if the function is not given any inputs. future<decay_t<T>>, valid() == true
.future<decay_t<T>>, is_ready() == true
.make_exceptional_future
A new section 30.6.13 shall be inserted at the end of