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 ready
bool 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
async
call ([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*this
is associated with apromise
object, the completion ofset_value()
orset_exception()
to thatpromise
happens before (1.10)get()
returns.
Delete member functions is_ready
, has_exception
,
and has_value
.
bool is_ready() const;
Returns:true
only 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:true
only ifis_ready() == true
and the associated state contains an exception.bool has_value() const;
Returns:true
only ifis_ready() == true
and 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
*this
is ready.
Edit paragraph 14 as follows.
Synchronization: if
*this
is associated with apromise
object, the completion ofset_value()
orset_exception()
to thatpromise
happens before (1.10)wait()
returns. If the future is associated with a thread created by anasync
call ([futures.async]), as if associated-thread.join()
.
Edit the wait_for()
prototype as follows.
template <class Rep, class
periodPeriod> 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
*this
is ready or untilrel_time
has elapsed. If*this
is 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 ready
bool 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
async
call ([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:true
only if the associated state holds a value or an exception ready for retrieval.bool has_exception() const;Returns:true
only ifis_ready() == true
and the associated state contains an exception.bool has_value() const;Returns:true
only ifis_ready() == true
and 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
*this
is ready.
Edit paragraph 14 as follows.
Synchronization: if
*this
is associated with apromise
object, the completion ofset_value()
orset_exception()
to thatpromise
happens before (1.10)get()
returns. If the future is associated with a thread created by anasync
call ([futures.async]), as if associated-thread.join()
.
Edit the wait_for()
prototype as follows.
template <class Rep, class
periodPeriod> 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
*this
is ready or untilrel_time
has 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:
F
and each typeTi
inArgs
shall beCopyConstructible
if 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 thepolicy
parameter is not present, then as if the policy parameter werelaunch::any
. Ifpolicy
islaunch::async
, executesINVOKE (f, w1, w2, ..., wN)
as if in a newthread
of execution. Any return value is captured by theunique_future
. Any exception not caught byf
is captured by theunique_future
. Thethread
is associated with theunique_future
, and affects the behavior of theunique_future
. Ifpolicy
islaunch::sync
, thenINVOKE (f, w1, w2, ..., wN)
is associated with the returnedunique_future
. The invocation is said to be deferred. Ifpolicy
islaunch::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
async
happens before (1.10 [intro.multithread]) the invocation off
. [Note: This statement applies even when the correspondingunique_future
is moved to another thread. —end note]Throws:
std::system_error
ifpolicy
islaunch::async
and the implementation is unable to start a new thread.Error conditions: —
resource_unavailable_try_again
— ifpolicy
islaunch::async
and 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
work
below 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 forcingwork2
to be evaluated beforework1()
. —end note:]