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 and std::packaged_task
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 T>
future<decay_t<T>> make_ready_future(T&& value);
future<void> make_ready_future();
future<T> make_exceptional_future(exception_ptr ex);
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 Sequence>
struct when_any_result;
template <class InputIterator>
see below when_any(InputIterator first, InputIterator last);
template <class... Futures>
see below when_any(Futures&&... futures);
} // 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&) = delete;
future(future<future<R>>&&) noexcept;
~future();
future& operator=(const future&) = 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 noexcept;
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);
};
} // namespace concurrency_v1
} // namespace experimental
} // namespace std
In
future object from the shared state referred to by
rhsfuturefuture becomes ready when one of the following occurs:
futures are ready. The future stores the value or the exception from the inner future.
future is ready but the inner future is invalid. The future stores an exception of type std::future_error, with an error condition 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, which will be executed
as specified below.
INVOKE(DECAY_COPY (std::forward<F>(func)), std::move(*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 as a parameter.
future object. The further behavior of the function 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).INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this)) is called on
an unspecified thread of execution with the call to
DECAY_COPY() being evaluated in the thread that called
then.
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 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.
The type of 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;
});
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 condition of std::future_errc::broken_promise.
true if the shared state is ready, otherwise false.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<R>&&) noexcept;
shared_future(shared_future&&) noexcept;
shared_future(future<shared_future<R>>&& rhs) noexcept;
~shared_future();
shared_future& operator=(const shared_future&);
shared_future& operator=(shared_future&&) noexcept;
// retrieving the value
see below get();
// functions to check state
bool valid() const noexcept;
bool is_ready() const noexcept;
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;
};
} // 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 stores the value or the exception from the inner shared_future.
future is ready but the inner shared_future is invalid.
The shared_future stores an exception of type std::future_error, with an error condition 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, which will be executed
as specified below.
INVOKE(DECAY_COPY (std::forward<F>(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 as the second parameter.shared_future object as a parameter.
future object. The further behavior of the function 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).INVOKE(DECAY_COPY(std::forward<F>(func)), *this) is called on
an unspecified thread of execution with the call to
DECAY_COPY() being evaluated in the thread that called
then.
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
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 condition of std::future_errc::broken_promise.
true if the shared state is ready, otherwise false.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 (
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 must be future<R>
or shared_future<R>, for some type R.
T is of type future<R> or shared_future<R>futures and shared_futures passed into
when_all must be in a valid state (i.e. valid() == true).
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
first == 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.Ui be
decay_t<Fi> for each Fi in
Futures. This function shall not participate in overload resolution unless each
Ui is either future<Ri>
or shared_future<Ri>.
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.
Sequence is
created, where Sequence is either tuple or a
vector based on the overload, as specified above.
A new future object that refers to that shared state is created
and returned from when_all.
futures and shared_futures supplied
to the call to when_all are ready, the futures
are moved, and the shared_futures are copied,
into, correspondingly, futures or shared_futures
of the futures member of Sequence in the shared state.when_all.future returned by when_all will not throw an exception, but the
futures and shared_futures held in the shared state may.valid() == true.futures, valid() == false.shared_futures, valid() == true.future object that becomes ready when all the input
futures/shared_futures 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_result
The library provides a template for storing the result of when_any.
template<class Sequence>
struct when_any_result {
size_t index;
Sequence futures;
};
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 must be future<R>
or shared_future<R>, for some type R.
T is of type future<R> or shared_future<R>futures and shared_futures passed into
when_any must be in a valid state (i.e. valid() == true).
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.when_any where
first == last,
returns a future that is immediately ready.
The value of the index field of the when_any_result is
unspecified. The futures field is an empty vector.
when_any with no arguments returns a
future that is immediately ready.
The value of the index field of the when_any_result is
unspecified. The futures field is tuple<>.
Ui be
decay_t<Fi> for each Fi in
Futures. This function shall not participate in overload resolution unless each
Ui is either future<Ri>
or shared_future<Ri>.
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.when_any_result<Sequence> is created,
where Sequence is a vector for the first overload and a tuple for the second overload.
A new future object that refers to that shared state is created and returned
from when_any.
futures or shared_futures
supplied to the call to when_any is ready, the futures
are moved, and the shared_futures are copied
into, correspondingly, futures or shared_futures
of the futures member of Sequence in the shared state.when_any.
future returned by when_any will not throw
an exception, but the futures and shared_futures
held in the shared state may.
valid() == true.futures, valid() == false.shared_futures, valid() == true.future object that becomes ready when any of the input
futures/shared_futures 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
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 returned by when_any_back will not throw
an exception, but the futures and shared_futures
held in the shared state 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<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
Let U be decay_t<T>. Then V is X& if U equals
reference_wrapper<X>, otherwise V is U.
future if it
is an rvalue. Otherwise the value is copied to the shared state of the returned
future.future.
future<V>, if function is given a value of type T.future<void>, if the function is not given any inputs.future, valid() == true.future, is_ready() == true.make_exceptional_future
A new section 30.6.13 shall be inserted at the end of