ISO/IEC JTC1 SC22 WG21 N2996 = 09-0186 - 2009-10-23
Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
Herb Sutter, hsutter@microsoft.com
This paper is a consensus of "A simple async() (revision 1)" N2970 = 09-0160 - 2009-09-26 and "An Asynchronous Call for C++" N2973 = 09-0163 - 2009-09-27.
Introduction
Acknowledgements
Proposed Wording
30.6.1 Overview [futures.overview]
30.6.5 Class template unique_future [futures.unique_future]
30.6.6 Class template shared_future [futures.shared_future]
30.6.? Function template async [futures.async]
This paper represents a consensus proposal on an asynchronous execution facility. For rationale see papers N2970 and N2973. For further background see paper N2974.
The following changes have been made with respect to N2970 and N2973.
is_ready, has_exception,
and has_value query functions.
The presence of these function requires still other functions
in the synchronous case.
Evolution Working Group direction is that
the complexity in interface is as yet unjustified,
and prudence dictates a smaller initial interface.
unique_future
was created from a deferred async.
unique_future to shared_future
will execute any deferred work.
enum class
to better clarify the launching policy.
This solution derives from an extensive discussion on the C++ threads standardisation <cpp-threads@decadentplace.org.uk> mailing list. Thanks to the following contributors to the discussion on this topic: Hans Boehm, Beman Dawes, Peter Dimov, Pablo Halpern, Howard Hinnant, Oliver Kowalke, Doug Lea, Arch Robison, Bjarne Stroustrup, Alexander Terekhov, and Anthony Williams. Further discussion at the October 2009 meeting include contributors beyond our ability to recount. We gratefully acknowledge their contribution.
The proposed wording is as follows. It consists primarily of two new subsections.
Add to the synopsis the appropriate entries from the following sections.
unique_future [futures.unique_future]
Edit the synopsis as follows.
The edit removes query functions on unique_future.
namespace std { template <class R> class unique_future { public: unique_future(unique_future &&); unique_future(const unique_future& rhs) = delete; ~unique_future(); unique_future& operator=(const unique_future& rhs) = delete; // retrieving the value see below get(); // functions to check state and wait for readybool is_ready() const; bool has_exception() const; bool has_value() const;void wait() const; template <class Rep, class Period> bool wait_for( const chrono::duration<Rep, Period>& rel_time) const; template <class Clock, class Duration> bool wait_until( const chrono::time_point<Clock, Duration>& abs_time) const; }; }
After paragraph 4, add a new paragraph as follows. This paragraph is a requirement on the destructor.
Synchronization: If no other future refers to the associated state, and that state is associated with a thread created by an
asynccall ([futures.async]), as if associated-thread.join().
After paragraph 5, add a new paragraph as follows.
This paragraph is a requirement on get().
Effects: As if
wait().
Delete paragraph 6.
This synchronization now happens as a consequence
of "as if" wait().
Synchronization: if*thisis associated with apromiseobject, the completion ofset_value()orset_exception()to thatpromisehappens before (1.10)get()returns.
Delete member functions is_ready, has_exception,
and has_value.
bool is_ready() const;
Returns:trueonly if the associated state holds a value or an exception ready for retrieval.
Remark: the return value is unspecified after a call toget().bool has_exception() const;Returns:trueonly ifis_ready() == trueand the associated state contains an exception.bool has_value() const;Returns:trueonly ifis_ready() == trueand the associated state contains a value.
Edit paragraph 13 as follows.
Effects: if the associated state contains a deferred function, then execute the deferred function. Otherwise, blocks until
*thisis ready.
Edit paragraph 14 as follows.
Synchronization: if
*thisis associated with apromiseobject, the completion ofset_value()orset_exception()to thatpromisehappens before (1.10)wait()returns. If the future is associated with a thread created by anasynccall ([futures.async]), as if associated-thread.join().
Edit the wait_for() prototype as follows.
template <class Rep, classperiodPeriod> void wait_for(const chrono::duration<Rep, Period>& rel_time) const;
Edit paragraph 16 as follows.
Effects: if the associated state contains a deferred function, then the behavior is unspecified. Otherwise, blocks until
*thisis ready or untilrel_timehas elapsed. If*thisis ready, as ifwait().
shared_future [futures.shared_future]Edit the synopsis as follows.
namespace std { template <class R> class shared_future { public: shared_future(const shared_future& rhs); shared_future(unique_future<R>&&); ~shared_future(); shared_future & operator=(const shared_future& rhs) = delete; // retrieving the value see below get() const; // functions to check state and wait for readybool is_ready() const; bool has_exception() const; bool has_value() const;void wait() const; template <class Rep, class Period> bool wait_for(const chrono::duration<Rep, Period>& rel_time) const; template <class Clock, class Duration> bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const; }; }
After paragraph 5, add a new paragraph as follows. This paragraph is a note on the destructor.
Synchronization: If no other future refers to the associated state, and that state is associated with a thread created by an
asynccall ([futures.async]), as if associated-thread.join().
After paragraph 6, add a new paragraph as follows.
This paragraph is a requirement on get().
Effects: As if
wait().
Remove the member functions is_ready,
has_exception, has_value.
bool is_ready() const;Returns:trueonly if the associated state holds a value or an exception ready for retrieval.bool has_exception() const;Returns:trueonly ifis_ready() == trueand the associated state contains an exception.bool has_value() const;Returns:trueonly ifis_ready() == trueand the associated state contains a value.
Edit paragraph 13 as follows.
This paragraph describes wait().
Effects: if the associated state contains a deferred function, then execute the deferred function. Otherwise, blocks until
*thisis ready.
Edit paragraph 14 as follows.
Synchronization: if
*thisis associated with apromiseobject, the completion ofset_value()orset_exception()to thatpromisehappens before (1.10)get()returns. If the future is associated with a thread created by anasynccall ([futures.async]), as if associated-thread.join().
Edit the wait_for() prototype as follows.
template <class Rep, classperiodPeriod> void wait_for(const chrono::duration<Rep, Period>& rel_time) const;
Edit paragraph 16 as follows.
This paragraph describes wait_for().
Effects: if the associated state contains a deferred function, then the behavior is unspecified. Otherwise, blocks until
*thisis ready or untilrel_timehas elapsed.
async [futures.async]Add the following section.
enum class launch { any, async, sync };template<class F, class ...Args> unique_future<typename F::result_type> async( F&& f, Args&&... args );template<class F, class ...Args> unique_future<typename F::result_type> async( launch policy, F&& f, Args&&... args );Requires:
Fand each typeTiinArgsshall beCopyConstructibleif an lvalue and otherwiseMoveConstructible.INVOKE (f, w1, w2, ..., wN)(20.7.2) shall be a valid expression for some valuesw1,w2, ...,wN, whereN == sizeof...(Args).Effects: Constructs an object of type
unique_future<typename F::result_type>([futures.unique_future]). If thepolicyparameter is not present, then as if the policy parameter werelaunch::any. Ifpolicyislaunch::async, executesINVOKE (f, w1, w2, ..., wN)as if in a newthreadof execution. Any return value is captured by theunique_future. Any exception not caught byfis captured by theunique_future. Thethreadis associated with theunique_future, and affects the behavior of theunique_future. Ifpolicyislaunch::sync, thenINVOKE (f, w1, w2, ..., wN)is associated with the returnedunique_future. The invocation is said to be deferred. Ifpolicyislaunch::any, the implementation may choose either policy above at any call toasync. [Note: Implementations should defer invocations when no more concurrency can be effectively exploited. —end note]Synchronization: The invocation of the
asynchappens before (1.10 [intro.multithread]) the invocation off. [Note: This statement applies even when the correspondingunique_futureis moved to another thread. —end note]Throws:
std::system_errorifpolicyislaunch::asyncand the implementation is unable to start a new thread.Error conditions: —
resource_unavailable_try_again— ifpolicyislaunch::asyncand either the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.[Example: Two items of
workbelow may be executed concurrently.extern int work1(int value); extern int work2(int value); int work(int value) { auto handle = std::async( [=]{ return work2(value); } ); int tmp = work1(value); return tmp + handle.get(); }—end example:] [Note: The statement
return work1(value) + handle.get();might not result in concurrency because
get()may be evaluated beforework1(), thus forcingwork2to be evaluated beforework1(). —end note:]