Document number: N4313 Revises: N4123 Date: 2014-11-21 Project: Programming Language C++ Reference: ISO/IEC IS 14882:2011(E) Reply to: Artur Laksberg, Microsoft Corp. arturl@microsoft.com
This document proposes improvements to N4107, the current Working Draft of the Concurrency TS.
This document is a revision of N4123, which was presented to SG1 and LEWG at the Urbana meeting.
This document is motivated by N4032 and N4048, which were presented to SG1 at the Rapperswil meeting. This document collects the parts of these papers that received positive feedback from SG1 and presents them as a combined set of proposed changes to the Concurrency TS.
The paper also addresses numerous issues discovered and reported to the author by various individuals, namely Agustín Bergé, Vicente J. Botet Escriba, Hartmut Kaiser and Anthony Williams.
In addition to the changes proposed in N4123, this document implements the following suggestions from SG1 and LEWG received at the Urbana meeting:
std::experimental::async function. This avoids
    having to specify execution policies. This also
    avoids the question of whether a future created by 
    async should block in the destructor.
  when_any_back, and change the return type of 
    when_any to return the sequence of futures 
    as well as the index of the ready future.
  
Finally, the paper implements a small fix to the signature of make_ready_future
to support reference_wrapper types. This fix was 
requested
during the Urbana meeting but was not reviewed by SG1 or LEWG.
The author seeks feedback from LWG on this document. If approved by LWG, the changes proposed in this document will be incorporated into the next Working Paper of the Concurrency TS.
The proposed changes are presented as "diffs" to N4107 marked as insertions and
deletions.
 Some of the extensions described in this Technical Specification represent
types and functions that are currently not part of the C++ Standards Library,
and because these extensions are experimental, they should not be declared
directly within namespace 
std. Instead, such extensions are
declared in namespace std::experimental.
std.
    — end note ]
  
Unless otherwise specified, references to such entities described in this
Technical Specification are assumed to be qualified with
std::experimental, and references to entities described in the C++
Standard Library are assumed to be qualified with std::.
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 | 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 and std::packaged_task
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 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 stdfuture
The specification of all declarations within this sub-clause 
bool is_ready() const;
future(future<future<R>>&&) noexcept;
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 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(future<future<R>>&& rhs) noexcept; 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.
template <typenameclass F>
see below then(F&& func);
template <typename F>
see below then(launch policy, F&& func);
 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.
      — end example ]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;
                 });
        
    — end example ]
  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.
      
    bool is_ready() const noexcept; true if the shared state is ready, otherwise false.shared_futurebool 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 stdshared_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 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.
template <typenameclass F>
see below then(F&& func) const;
template <class F>
see below then(launch policy, F&& func) const;
 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_futurefuture. 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_futurethen.
    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.
      
    bool is_ready() const noexcept; 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.
template <class InputIterator>
see below
future<vector<typename iterator_traits<InputIterator>::value_type>>
when_all(InputIterator first, InputIterator last);
template <typenameclass... TFutures>
see below
future<tuple<decay_t<Futures>...>> when_all(TFutures&&... futures);
 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.
template <class InputIterator>
see below
future<when_any_result<vector<typename iterator_traits<InputIterator>::value_type>>>
when_any(InputIterator first, InputIterator last);
template <typenameclass... TFutures>
see below
future<when_any_result<tuple<decay_t<Futures>...>>> when_any(Futures&&... futures);
 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 
template <class InputIterator>
see below
when_any_back(InputIterator first, InputIterator last);
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 
template <class T>
future<V> make_ready_future(T&& value);
future<void> make_ready_future();
   
  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 
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();