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.
future
s and shared_future
s 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.future
s are moved, and any shared_future
s
are copied into, correspondingly, future
s or
shared_future
s of
Sequence
in the shared state.
when_all
.
future
s and shared_future
s supplied
to the call to when_all
are ready, the resulting future
,
as well as the future
s and shared_future
s
of the Sequence
, are ready.
future
returned by when_all
will not store an exception, but the
shared states of future
s and shared_future
s held in the shared state may.future
, valid() == true
.future
s, valid() == false
.shared_future
s, valid() == true
.future
object that becomes ready when all of the input
future
sand shared_future
s 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.
future
s and shared_future
s 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<>
.
future
s are moved, and any shared_future
s
are copied into, correspondingly, future
s or
shared_future
s 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
.
future
s or shared_future
s 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 future
s and shared_future
s held in the shared state may.future
, valid() == true
.future
s, valid() == false
.shared_future
s, valid() == true
.future
object that becomes ready when any of the input
future
s and shared_future
s 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