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>
see below make_ready_future(T&& value);
future<void> make_ready_future();
template <class T>
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 specifications of all declarations within this subclause
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;
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
future object from the shared state referred to by
rhs.
The future becomes ready when one of the following occurs:
rhs and rhs.get() are ready. The value or the exception from rhs.get() is stored in the future's shared state.
rhs is ready but rhs.get() is invalid. An exception of type std::future_error, with an error condition of std::future_errc::broken_promise is stored in the future's shared state.
valid() == true.rhs.valid() == false.
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. Additionally,
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.
result_of_t<decay_t<F>(future<R>)>
is future<R2>, for some type R2, the function returns future<R2>.
Otherwise, the function returns future<result_of_t<decay_t<F>(future<R>)>>.
then taking a callable 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;
});
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 specifications of all declarations within this subclause
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(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;
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
shared_future object from the shared state referred to by
rhs.
The shared_future becomes ready when one of the following occurs:
rhs and rhs.get() are ready. The value or the exception from rhs.get() is stored in the shared_future's shared state.
rhs is ready but rhs.get() is invalid.
The shared_future stores an exception of type std::future_error, with an error condition of std::future_errc::broken_promise.
valid() == true.rhs.valid() == false.
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.future object. Additionally,
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.
result_of_t<decay_t<F>(const shared_future&)>
is future<R2>, for some type R2, the function returns future<R2>.
Otherwise, the function returns future<result_of_t<decay_t<F>(const shared_future&)>>.
future. See the notes on
the return type of future::then in valid() == true on the original shared_future object.
valid() == true on the 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 specifications of all declarations within this subclause
The future returned by the function get_future is the one defined in the experimental
namespace (
packaged_task
The specifications of all declarations within this subclause
The future returned by the function get_future is the one defined in the experimental
namespace (
when_all
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.
futures and shared_futures passed into
when_all must be in a valid state (i.e. valid() == true).
iterator_traits<InputIterator>::value_type is future<R>
or shared_future<R> for some type R.
Di be
decay_t<Fi>, and
let Ui be
remove_reference_t<Fi>
for each Fi in
Futures. This function shall not participate in overload resolution unless
for each i either Di
is a shared_future<Ri>
or Ui is a future<Ri>.
Sequence is
created, where Sequence is either vector or
tuple based on the overload, as specified above.
A new future object that refers to that shared state is created
and returned from when_all.
first == last, when_all
returns a future with an empty vector that is immediately
ready.
when_all returns a future<tuple<>>
that is immediately ready.futures are moved, and any shared_futures
are copied into, correspondingly, futures or
shared_futures of
Sequence in the shared state.
when_all.
futures and shared_futures supplied
to the call to when_all are ready, the resulting future,
as well as the futures and shared_futures
of the Sequence, are ready.
future returned by when_all
will not store an exception, but the
shared states of futures and shared_futures held in the shared state may.future, valid() == true.futures, valid() == false.shared_futures, valid() == true.future object that becomes ready when all of the input
futuresand shared_futures are ready.
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
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.
futures and shared_futures passed into
when_all must be in a valid state (i.e. valid() == true).
iterator_traits<InputIterator>::value_type is future<R>
or shared_future<R> for some type R.
Di be
decay_t<Fi>, and
let Ui be
remove_reference_t<Fi>
for each Fi in
Futures. This function shall not participate in overload resolution unless
for each i either Di
is a shared_future<Ri>
or Ui is a future<Ri>.
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.
first == last,
when_any returns a future that is immediately ready.
The value of the index field of the when_any_result is
static_cast<size_t>(-1). The futures field is an empty vector.
when_any returns a future that is immediately ready.
The value of the index field of the when_any_result is
static_cast<size_t>(-1).
The futures field is tuple<>.
futures are moved, and any shared_futures
are copied into, correspondingly, futures or
shared_futures of the futures member of
when_any_result<Sequence> in the shared state.
futures shared state matches the order
of the arguments supplied to when_any.
futures or shared_futures supplied
to the call to when_any is ready, the resulting future
is ready.
Given the result future f,
f.get().index is the position of the ready future
or shared_future in the
futures member of
when_any_result<Sequence> in the shared state.
future returned by when_all
will not store an exception, but the
shared states of futures and shared_futures held in the shared state may.future, valid() == true.futures, valid() == false.shared_futures, valid() == true.future object that becomes ready when any of the input
futures and shared_futures are ready.
make_ready_future
Let U be decay_t<T>. Then V is X& if U equals
reference_wrapper<X>, otherwise V is U.
future associated
with that shared state.
For the first overload, the type of the shared state is V and the result is
constructed from std::forward<T>(value).
For the second overload, the type of the shared state is void.
future, valid() == true and is_ready() == true.
make_exceptional_future