Document Number: | N2967=09-0157 |
Date: | 2009-09-27 |
Project: | Programming Language C++ |
Detlef Vollmann <dv@vollmann.ch>
Anthony Williams <anthony@justsoftwaresolutions.co.uk>
This paper addresses a lot of editor's notes in N2798 and NB comments on clause 30.5 Futures (now 30.6).
This paper reflects the consensus after the discussion in Frankfurt. After that there was more discussion on the reflector, but this paper should provide a base for the discussion in Santa Cruz.
NB comment | LWG issue | Short description | Resolution |
---|---|---|---|
UK 330 | Reference to undefined constructible_with_allocator_prefix |
References were removed | |
JP 79 | Concepts for uses_allocator |
We don't have concepts now | |
UK 331 | 1160 | exposition only for error_code constructor |
Constructor is now properly defined |
UK 332 | Missing introduction to unique_future |
An introduction was added | |
UK 333 | Conceptifying *_future::get |
We don't have concepts now | |
UK 334 | 1047 | Missing blocking specification for *_future::get |
Blocking was now specified |
UK 335 | 1048 | Moved out state of a unique_future could not be detected |
valid() was added |
UK 336 | 1161 | Unnecessary unique_future limitations |
Default construction and move assignment were added |
JP 80 | Typo in *_future::wait_for |
Corrected | |
UK 337 | 1162 | shared_future should support an efficient move constructor |
Move constructor was added |
UK 338 | 1163 | shared_future is inconsistent with shared_ptr |
Move constructor and move assignment and
The need for an |
UK 339 | 1049 | promise move assignment goes into wrong direction |
Corrected |
UK 340 | 1050 | Missing postcondition on promise::get_future() |
Postcondition added |
UK 341 | Wrong parameter type for promise::swap() |
Corrected | |
UK 341 | Wrong parameter type for promise::swap() |
Corrected | |
UK 342 | 1088 | Non-member swap for promise::swap() |
Added |
UK 343 | 1165 | Unneeded promise move constructor |
Removed |
JP 81 | Conceptification of packaged_task |
We don't have concepts now, but uses_allocator trait was added |
As in the rewording nearly every paragraph of [future] has changed, here is the new complete proposed wording for that clause.
[futures] describes components that a C++ program can use to retrieve in one thread the result (value or exception) from a function that has run in another thread.
[Note: these components are not restricted to multi-threaded programs but can be useful in single-threaded programs as well. --end note]
namespace std { enum class future_errc { broken_promise, future_already_retrieved, future_already_initialized, promise_already_satisfied, no_state }; template <> struct is_error_code_enum<future_errc> : public true_type {}; constexpr error_code make_error_code(future_errc e); constexpr error_condition make_error_condition(future_errc e); extern const error_category* const future_category; class future_error; template <class R> class promise; template <class R> class promise<R&>; template <> class promise<void>; template <class R> class unique_future; template <class R> class unique_future<R&>; template <> class unique_future<void>; template <class R> class shared_future; template <class R> class shared_future<R&>; template <> class shared_future<void>; template <class R> class atomic_future; template <class R> class atomic_future<R&>; template <> class atomic_future<void>; template <class> class packaged_task; // undefined template <class R, class... ArgTypes> class packaged_task<R(ArgTypes...)>; }
extern const error_category* const future_category;
future_category
shall point to a statically initialized
object of a type derived from class error_category
.
default_error_condition
and equivalent
virtual functions shall behave as specified for the class
error_category
.
The object's name
virtual function shall return a pointer to the string "future".
constexpr error_code make_error_code(future_errc e);
error_code(static_cast<int>(e), *future_category)
.
constexpr error_condition make_error_condition(future_errc e);
error_condition(static_cast<int>(e), *future_category)
.
future_error
[futures.future_error]namespace std { class future_error : public logic_error { public: future_error(error_code ec); const error_code& code() const throw(); const char* what() const throw(); }; }
future_error(error_code ec);
future_error
.
Postcondition: ec == code()
const error_code& code() const throw();
ec
that was passed to the object's constructor.
const char *what() const throw();
code().message()
.
If fully initialized,
objects of type unique_future
([future.unique_future]),
shared_future
([future.shared_future]),
atomic_future
([future.atomic_future]),
promise
([future.promise) and
packaged_task
([future.task]) reference
some state that is potentially shared between several such objects.
This associated state consists of some state information and some (possibly not yet evaluated) result, which can be a (possibly void) value or an exception.
The result of such an associated state can be set by
promise::set_value
promise::set_exception
packaged_task::operator()
When the last reference to an associated state is given up, any resources held by this associated state are released.
An associated state is ready only if it holds a value or an exception ready for retrieval.
The functions that successfully set the stored result of an associated state synchronize with ([intro.multithread]) calls to member functions of other objects referring to the same associated state and such calls are serialized. The storage of the result (whether normal or exceptional) into the associated state happens-before ([intro.multithread]) that state is set to ready.
Accesses to the same associated state through member functions of
unique_future
, shared_future
,
atomic_future
, promise
or packaged_task
objects conflict ([intro.multithread]).
promise
[futures.promise]namespace std { template <class R> class promise { public: promise(); template <class Allocator> promise(allocator_arg_t, const Allocator& a); promise(promise && rhs); template <class Allocator> promise(allocator_arg_t, const Allocator& a, promise& rhs); promise(const promise& rhs) = delete; ~promise(); // assignment promise & operator=(promise&& rhs); promise & operator=(const promise& rhs) = delete; void swap(promise& other); // retrieving the result unique_future<R> get_future(); // setting the result void set_value(const R& r); void set_value(see below); void set_exception(exception_ptr p); }; template <typename R, class Alloc> struct uses_allocator<promise<R>, Alloc>; }
The implementation shall provide the template promise
and two specializations, promise<R&>
and promise<void>
.
These differ only in the argument type of the member function
set_value
, as set out in its description, below.
template <typename R, class Alloc> struct uses_allocator<promise<R>, Alloc> : true_type { };
Alloc
shall be an Allocator ([allocator.requirements] 20.1.2)
promise
can be constructed with an allocator, even
though it does not have a nested allocator_type
associated type.
--end note]
promise(); template <class Allocator> promise(allocator_arg_t, const Allocator& a);
promise
object and an associated state.
The second constructor uses the allocator a
to allocate memory for the associated state.
promise(promise && rhs); template <class Allocator> promise(allocator_arg_t, const Allocator& a, promise& rhs);
promise
object and
transfers ownership of the associated state of rhs
(if
any) to the newly-constructed object.
rhs
has no associated state.
~promise();
*this
is
not ready, stores an exception of
type future_error
with an error code
of broken_promise
as-if
by this->set_exception(copy_exception(future_error(future_errc::broken_promise)))
.
*this
and releases its reference to its
associated state if any. If this is the last reference to that
associated state, destroys that state.
promise& operator=(promise&& rhs);
std::promise<R>(std::move(rhs)).swap(*this);
rhs
has no associated state. *this
has
the associated state of rhs
prior to the assignment.
*this
.
void swap(promise& other);
*this
and other
.
Postcondition:
*this
has the same associated state (if any)
as other
prior to the call
to swap
. other
has the same associated
state (if any) as *this
prior to the call
to swap
.
unique_future<R> get_future();
unique_future<R>
object with the same associated state
as *this
.
future_error
if *this
has no associated
state, or if get_future()
has already been called on
a promise
with the associated state
of *this
.
future_already_retrieved
if get_future
has already been called on
a promise
with the associated state
of *this
.no_state
if *this
has no associated
state.void set_value(const R& r); void promise::set_value(R&& r); void promise<R&>::set_value(R& r); void promise<void>::set_value();
r
in the associated state and sets that
state to ready. Any threads blocked in a call of a blocking function
of any future that refers to the same associated
state as *this
are unblocked.
future_error
if its associated state is already
ready.
promise_already_satisfied
if its associated state is already
ready.no_state
if *this
has no associated
state.set_value
and set_exception
on
a single promise
object are serialized.
void set_exception(exception_ptr p);
p
in the associated state and sets that
state to ready. Any threads blocked in a call of a blocking function
of any future that refers to the same associated
state as *this
are unblocked.
future_error
if its associated state is already
ready.
promise_already_satisfied
if its associated state is already
ready.no_state
if *this
has no associated
state.set_value
and set_exception
on
a single promise
object are serialized.
unique_future
[futures.unique_future]
The class template unique_future
defines a type for
asynchronous return objects which do not share their associated
state. A default-constructed unique_future
has no
associated state. unique_future
objects with associated
state can only be created by use of
promise
([future.promise]) or packaged_task
([future.task]) objects, and share their associated state with
that promise
or packaged_task
. Their
values or exceptions can be set by use of the promise
([future.promise]) or packaged_task
([future.task])
object that shares the same associated state.
[Note:
unique_future
member functions do not synchronize with
themselves or with member functions of shared_future
.
--end note]
The effect of calling any member function other than the destructor
or the move-assignment operator on a unique_future
for which valid() == false
is undefined.
namespace std { template <typename R> class unique_future { public: unique_future(); unique_future(unique_future &&); unique_future(const unique_future & rhs) = delete; ~unique_future(); unique_future& operator=(const unique_future & rhs) = delete; unique_future& operator=(unique_future&& rhs); // 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; bool valid() 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; }; }
The implementation shall provide the template unique_future
and two specializations, unique_future<R&>
and unique_future<void>
.
These differ only in the return type and return value of the member function
get
, as set out in its description, below.
unique_future();
unique_future
that doesn't
refer to an associated state.
valid() == false
unique_future(unique_future&& rhs);
unique_future
object that refers to the
associated state that was originally referred to by rhs
(if any).
valid
returns the same value as rhs.valid()
prior to the constructor invocation.rhs.valid() == false
.~unique_future();
*this
.unique_future& operator=(unique_future&& rhs);
*this
referred to an associated state prior to
the assignment it gives up this reference.rhs
to*this
.valid
returns the same value as rhs.valid()
prior to the constructor invocation.rhs.valid() == false
.R&& get(); R& unique_future<R&>::get(); // this is for the specialization unique_future<R&> void unique_future<void>::get(); // this is for the specialization unique_future<void>
get
.
valid() == true
wait()
s until is_ready()
.unique_future::get()
returns an rvalue-reference to the
value stored in the object's associated state.unique_future<R&>::get()
returns the reference
stored as value in the object's associated state.unique_future<void>::get()
returns nothing.valid() == false
bool is_ready() const;
Precondition:
valid() == true
true
only if the associated state holds a value or an exception
ready for retrieval.
is_ready()
returns true
, the setting of the
object's associated state happens before ([intro.multithread])
is_ready()
returns.
is_ready() == true
.
bool has_exception() const;
valid() == true
true
only if is_ready()
returns true
and the associated state contains an exception.
bool has_value() const;
valid() == true
true
only if is_ready()
returns true
and the associated state contains a value.
bool valid() const;
true
only if *this
refers to an associated state.
void wait() const;
valid() == true
is_ready()
returns true
.
template <class Rep, class Period> bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;
valid() == true
is_ready()
returns true
or until rel_time
has elapsed.
is_ready()
.
template <class Clock, class Duration> bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
valid() == true
is_ready()
returns true
or until the current time exceeds abs_time
.
is_ready()
.
shared_future
[future.shared_future]
The class template shared_future
defines a type for
asynchronous return objects which may share their associated state. A
default-constructed shared_future
has no associated
state. A shared_future
object with associated state can
only be created from another shared_future
with
associated state or a unique_future
object with
associated state. Their values or exceptions can be set by use of
a promise
([future.promise])
or packaged_task
([future.task]) object that shares the
same associated state.
[Note:shared_future
member functions do not
synchronize with themselves, but they synchronize on the shared
associated state. --end note]
namespace std { template <class R> class shared_future { public: shared_future(); shared_future(const shared_future& rhs); shared_future(unique_future<R>&&); shared_future(shared_future&&); ~shared_future(); shared_future & operator=(const shared_future& rhs); shared_future & operator=(shared_future&& rhs); // 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; bool valid() 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; }; }
The implementation shall provide the template shared_future
and two specializations, shared_future<R&>
and shared_future<void>
.
These differ only in the return type and return value of the member function
get
, as set out in its description, below.
shared_future();
shared_future
that doesn't
refer to an associated state.
valid() == false
shared_future(const shared_future& rhs);
shared_future
object that refers to the same
associated state as rhs
(if any).
valid
returns the same value as rhs.valid()
shared_future(unique_future<R>&& rhs); shared_future(shared_future&& rhs);
shared_future
object that refers to the
associated state that was originally referred to by rhs
(if any).
valid
returns the same value as rhs.valid()
prior to the constructor invocation.rhs.valid() == false
.~shared_future();
*this
.shared_future& operator=(shared_future&& rhs);
*this
referred to an associated state prior to
the assignment it gives up this reference.rhs
to*this
.valid
returns the same value as rhs.valid()
prior to the constructor invocation.rhs.valid() == false
.shared_future& operator=(shared_future&& rhs);
*this
referred to an associated state prior to
the assignment it gives up this reference.rhs
to*this
.
[Note: So *this
refers to the same associated
state as rhs
(if any). --end note]valid
returns the same value as rhs.valid()
const R& shared_future::get() const; R& shared_future<R&>::get() const; void shared_future<void>::get() const;
get
.
valid() == true
wait()
s until is_ready()
.shared_future::get()
returns a const reference to the
value stored in the object's associated state.shared_future<R&>::get()
returns the reference
stored as value in the object's associated state.unique_future<void>::get()
returns nothing.bool is_ready() const;
true
only if valid()
returns
true
and the associated state holds a value or an exception
ready for retrieval.
is_ready()
returns true
, the setting of the
object's associated state happens before ([intro.multithread])
is_ready()
returns.
is_ready() == true
.
bool has_exception() const;
true
only if is_ready()
returns true
and the associated state contains an exception.
bool has_value() const;
true
only if is_ready()
returns true
and the associated state contains a value.
bool valid() const;
true
only if *this
refers to an associated state.
void wait() const;
is_ready()
returns true
.
template <class Rep, class Period> bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;
is_ready()
returns true
or until rel_time
has elapsed.
is_ready()
.
template <class Clock, class Duration> bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
is_ready()
returns true
or until the current time exceeds abs_time
.
is_ready()
.
atomic_future
[future.atomic_future]
The class template atomic_future
defines a type for
asynchronous return objects.
These objects can only be created by use of
promise
([future.promise])
or packaged_task
([future.task]) objects.
Their values or exceptions can be set
by use of promise
([future.promise]) objects.
Contrary to unique_future
and shared_future
,
calls to member functions of atomic_future
synchronize
with ([intro.multithread]) one another.
namespace std { template <class R> class atomic_future { public: atomic_future(); atomic_future(const atomic_future& rhs); atomic_future(unique_future<R>&& rhs); ~atomic_future(); atomic_future & operator=(const atomic_future& rhs); // 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; bool valid() 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; }; }
The implementation shall provide the template atomic_future
and two specializations, atomic_future<R&>
and atomic_future<void>
.
These differ only in the return type and return value of the member functions
get
and try_get
,
as set out in its description, below.
atomic_future();
atomic_future
that doesn't
refer to an associated state.
valid() == false
atomic_future(const atomic_future& rhs);
atomic_future
object that refers to the same
associated state as rhs
(if any).
valid
returns the same value as rhs.valid()
atomic_future(const unique_future<R>&& rhs);
atomic_future
object that refers to the
associated state that was originally referred to by rhs
(if any).
valid
returns the same value as rhs.valid()
prior to the constructor invocation.rhs.valid() == false
.~atomic_future();
*this
.atomic_future& operator=(const atomic_future & rhs);
this->valid() == false
rhs
to *this
.
[Note: So *this
refers to the same associated
state as rhs
(if any). --end note]
valid
returns the same value as rhs.valid()
future_error
with an error condition of
future_already_initialized
if the precondition isn't met.
const R& atomic_future::get() const; R& atomic_future<R&>::get() const; void atomic_future<void>::get() const;
get
.
valid() == true
wait()
s until is_ready()
.atomic_future::get()
returns an const reference to the
value stored in the object's associated state.atomic_future<R&>::get()
returns the reference
stored as value in the object's associated state.atomic_future<void>::get()
returns nothing.future_error
with an error condition of
no_state
if the precondition isn't met.
unique_future
, calling get()
more than once on the same object is well defined and just produces
the result again. --end note]
bool is_ready() const;
true
only if valid()
returns
true
and the associated state holds a value or an exception
ready for retrieval.
is_ready()
returns true
, the setting of the
object's associated state happens before ([intro.multithread])
is_ready()
returns.
is_ready() == true
.
bool has_exception() const;
true
only if is_ready()
returns true
and the associated state contains an exception.
bool has_value() const;
true
only if is_ready()
returns true
and the associated state contains a value.
bool valid() const;
true
only if *this
refers to an associated state.
void wait() const;
is_ready()
returns true
.
template <class Rep, class Period> bool wait_for(const chrono::duration<Rep, Period>& rel_time) const;
is_ready()
returns true
or until rel_time
has elapsed.
is_ready()
.
template <class Clock, class Duration> bool wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
is_ready()
returns true
or until the current time exceeds abs_time
.
is_ready()
.
packaged_task
[futures.task]
A packaged_task
provides a means of wrapping a
function or callable object so that the return value of the function
or callable object is stored in a future when it is invoked. The
associated state of a packaged_task
object includes
storage for a copy of this associated task.
When the packaged_task
is invoked, its associated task
is invoked, and the result (whether normal or exceptional) stored in
the associated state. Any futures that share the associated state
will then be able to access the stored result.
namespace std { template<class> class packaged_task; // undefined template<class R, class... ArgTypes> class packaged_task<R(ArgTypes...)> { public: typedef R result_type; // construction and destruction packaged_task(); template <class F> explicit packaged_task(F f); template <class F, class Allocator> explicit packaged_task(allocator_arg_t, const Allocator& a, F f); explicit packaged_task(R(*f)(ArgTypes...)); template <class F> explicit packaged_task(F&& f); template <class F, class Allocator> explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f); ~packaged_task(); // no copy packaged_task(packaged_task&) = delete; packaged_task& operator=(packaged_task&) = delete; // move support packaged_task(packaged_task&& other); packaged_task& operator=(packaged_task&& other); void swap(packaged_task& other); explicit operator bool() const; // result retrieval unique_future<R> get_future(); // execution void operator()(ArgTypes... ); void reset(); }; }
packaged_task();
packaged_task
object with no
associated state or task.
template <class F> packaged_task(F f); template <class F, class Allocator> explicit packaged_task(allocator_arg_t, const Allocator& a, F f); packaged_task(R(*f)(ArgTypes...)); template <class F> packaged_task(F&& f); template <class F, class Allocator> explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
INVOKE (f, t1, t2, ..., tN, R)
where t1
, t2
, ..., tN
are
values of the corresponding types in ArgTypes....
shall be a valid expression. Invoking a copy of f
shall behave the same as invoking f
.
packaged_task
object with a copy
of f
stored in the associated state as the object's
associated task. The constructors that take an Allocator
argument use it to allocate memory needed to store the internal data
structures.
f
,
or std::bad_alloc
if memory
for the associated state could not be allocated.
packaged_task(packaged_task&& other);
packaged_task
object and transfers ownership of other
's associated
state to *this
,
leaving other
with no associated state.
other
has no associated state.
packaged_task& operator=(packaged_task&& other);
std::packaged_task<R,ArgTypes...>(other).swap(*this);
~packaged_task();
*this
is
not ready, stores an exception of
type future_error
with an error code
of broken_promise
. Any threads blocked in a
member function
of unique_future
, shared_future
or atomic_future
waiting for the state associated
with *this
to become ready are unblocked.
*this
and releases its reference to its
associated state if any. If this is the last reference to that
associated state, destroys that state.
void swap(packaged_task& other);
*this
and other
.
*this
has the same associated state (if any)
as other
prior to the call
to swap
. other
has the same associated
state (if any) as *this
prior to the call
to swap
.
explicit operator bool() const;
true
if and only if *this
has an associated state.
unique_future<R> get_future();
unique_future
object that
shares the same associated state as *this
.
std::future_error
if an error occurs.
future_already_retrieved
if get_future
has already been called on
a packaged_task
with the associated state
of *this
.no_state
if *this
has no associated
state.void operator()(ArgTypes... args);
INVOKE (f, t1, t2, ..., tN, R)
,
where f
is the task in associated state
of *this
and t1
, t2
,
..., tN
are the values in args...
. If
the task returns normally, the return value is stored as the
asynchronous result in the state associated
with *this
, otherwise the exception thrown by the
task is stored. The state associated with *this
is
made ready, and any threads blocked in a member function
of unique_future
, shared_future
or atomic_future
waiting for the state associated
with *this
to become ready are
unblocked.
std::future_error
if there is no
associated state or the task has already been invoked.
no_state
if *this
has no associated
state.promise_already_satisfied
if the associated
state is already ready.operator()
synchronizes with
([intro.multithread]) a call to any member function of
a unique_future
object, shared_future
object or atomic_future
object that shares the same
associated state. The completion of the invocation of the
associated task and the storage of the result (whether normal or
exceptional) into the associated state happens-before
([intro.multithread]) that state is set to ready.
operator()
synchronizes and serializes with other functions
through the referred associated state. --end note]
void reset();
*this
by *this = packaged_task(std::move(f))
,
where f
is the task
stored in the associated state of *this
.
*this
. The old state is discarded, as described in
the destructor
for packaged_task
. get_future()
may now be
called again for *this
. -- End Note]
std::bad_alloc
if memory for the new
associated state could not be allocated.std::future_error
with an error condition
of no_state
if *this
has no
associated state