| Document Number: | |
|---|---|
| Date: | |
| Revises: | |
| Editor: | Microsoft Corp. |
Note: this is an early draft. It’s known to be incomplet and incorrekt, and it has lots of bad formatting.
Since the extensions described in this technical specification
are experimental and not part of the C++ standard library, they
should not be declared directly within namespace
std.
Unless otherwise specified, all components described in this technical specification either:
::experimental::concurrency_v1
to a namespace defined in the C++ Standard Library,
such as std, or
std.
Each header described in this technical
specification shall import the contents of
std::experimental::concurrency_v1 into
std::experimental as if by
namespace std {
namespace experimental {
inline namespace concurrency_v1 {}
}
}
Unless otherwise specified, references to other entities
described in this technical specification are assumed to be
qualified with std::experimental::concurrency_v1::,
and references to entities described in the standard are assumed
to be qualified with std::.
Extensions that are expected to eventually be added to an
existing header <meow> are provided inside the
<experimental/meow> header, which shall include
the standard contents of <meow> as if by
#include <meow>
New headers are also provided in the
<experimental/> directory, but without such an
#include.
|
This section describes tentative plans for future versions of this technical specification and plans for moving content into future versions of the C++ Standard.
The C++ committee intends to release a new version of this
technical specification approximately every year, containing the
library extensions we hope to add to a near-future version of the
C++ Standard. Future versions will define their contents in
std::experimental::concurrency_v2,
std::experimental::concurrency_v3, etc., with the
most recent implemented version inlined into
std::experimental.
When an extension defined in this or a future version of this
technical specification represents enough existing practice, it
will be moved into the next version of the C++ Standard by
removing the experimental::concurrency_vN
segment of its namespace and by removing the
experimental/ prefix from its header's path.
For the sake of improved portability between partial implementations of various C++ standards,
WG21 (the ISO technical committee for the C++ programming language) recommends
that implementers and programmers follow the guidelines in this section concerning feature-test macros.
Implementers who provide a new standard feature should define a
macro with the recommended name,
in the same circumstances under which the feature is available
(for example, taking into account relevant command-line options),
to indicate the presence of support for that feature.
Implementers should define that macro with the value specified in
the most recent version of this technical specification that they
have implemented.
The recommended macro name is "__cpp_lib_experimental_" followed by the string in the "Macro Name Suffix" column.
Programmers who wish to determine whether a feature is available in an implementation should base that determination on
the presence of the header (determined with __has_include(<header/name>))
and
the state of the macro with the recommended name.
(The absence of a tested feature may result in a program with
decreased functionality, or the relevant functionality may be provided
in a different way.
A program that strictly depends on support for a feature can just
try to use the feature unconditionally;
presumably, on an implementation lacking necessary support,
translation will fail.)
| Doc. No. | Title | Primary Section | Macro Name Suffix | Value | Header |
|---|---|---|---|---|---|
| N3875 | Improvements to std::future<T> and Related APIs | future_continuations |
201410 | <experimental/future> |
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.
#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(future<future<R>>&& rhs) noexcept;
future object from the shared state referred to by
rhs.
The future becomes ready when one of the following occurs:
futures 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() == 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.
template<class F>
see below then(F&& func);
template<class F>
see below then(launch policy, F&& func);
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
— end example ]
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>>:
— end example ]
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.
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.
bool is_ready() const;
true if the shared state is ready, false if it isn't.shared_future 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
shared_future(future<shared_future<R>>&& rhs) noexcept;
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() == true.rhs.valid() == false.
template<class F>
see below then(F&& func) const;
template<class F>
see below then(launch policy, F&& func) const;
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 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.
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 code of std::future_errc::broken_promise.
bool is_ready() const;
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
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.
template <class InputIterator>
see below
future<vector<typename iterator_traits<InputIterator>::value_type>>
when_all(InputIterator first, InputIterator last);
template <class... Futures>
future<tuple<decay_t<Futures>...>> when_all(Futures&&... futures);
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.
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_all with no arguments returns a
future<tuple<>> that is immediately ready.futures or shared_futures 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.
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 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
futures and shared_futures held in the output collection may.valid() == true.future<T>s valid() == false.shared_future<T> valid() == true.futures/shared_futures are ready.
when_anywhen_any creates a future object that
becomes ready when at least one element in a set of future and shared_future objects
becomes ready.
template <class InputIterator>
future<vector<typename iterator_traits<InputIterator>::value_type>>
when_any(InputIterator first, InputIterator last);
template <class... Futures>
future<tuple<decay_t<Futures>...>> when_any(Futures&&... futures);
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.
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.futures and shared_futures 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 futures or shared_futures 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.
futures or shared_futures
supplied to the call to when_any are ready, the futures
are moved, and the shared_futures are copied
into, correspondingly, futures or shared_futures
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 futures and shared_futures
held in the output collection may.
valid() == true.future<T>s valid() == false.shared_future<T> valid() == true.futures/shared_futures are ready.
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.
template <class InputIterator>
future<vector<typename iterator_traits<InputIterator>::value_type>>
when_any_back(InputIterator first, InputIterator last);
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.
futures and shared_futures 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 futures or shared_futures 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.
futures or shared_futures
supplied to the call to when_any_back are ready, the futures
are moved, and the shared_futures are copied
into, correspondingly, futures or shared_futures
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 futures and shared_futures
held in the output collection may.
valid() == true.future<T>s valid() == false.shared_future<T> valid() == true.futures/shared_futures are ready.
make_ready_future
A new section 30.6.13 shall be inserted at the end of
template <class T>
future<decay_t<T>> make_ready_future(T&& value);
future<void> make_ready_future();
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
template <class T>
future<T> make_exceptional_future(exception_ptr ex);
promise<T> p;
p.set_exception(ex);
return p.get_future();
template <class T, class E>
future<T> make_exceptional_future(E ex);
promise<T> p;
p.set_exception(make_exception_ptr(ex));
return p.get_future();