Thursday, 27 March 2008
I had resigned myself to Thread Pools and Futures being punted to TR2 rather than C++0x, but it seems there is potential for some movement on this issue. At the meeting of WG21 in Kona, Hawaii in October 2007 it was agreed to include asynchronous future values in C++0x, whilst excluding thread pools and task launching.
Detlef Vollman has rekindled the effort, and drafted N2561: An Asynchronous Future Value with myself and
Howard Hinnant, based on a discussion including other members of the Standards Committee. This paper proposes four templates:
shared_future, which are the asynchronous values themselves, and
promise, which provide ways of setting the asynchronous values.
Asynchronous future values
unique_future is very much like
unique_ptr: it represents exclusive ownership of the value. Ownership
of a (future) value can be moved between
unique_future instances, but no two
unique_future instances can
refer to the same asynchronous value. Once the value is ready for retrieval, it is moved out of the internal storage
buffer: this allows for use with move-only types such as
shared_future is very much like
shared_ptr: multiple instances can refer to the same
(future) value, and
shared_future instances can be copied around. In order to reduce surprises with this usage (with
one thread moving the value through one instance at the same time as another tries to move it through another instance), the stored
value can only be accessed via
const reference, so must be copied out, or accessed in place.
Storing the future values as the return value from a function
The simplest way to calculate a future value is with a
packaged_task<T>. Much like
encapsulates a callable object or function, for invoking at a later time. However, whereas
std::function returns the
result directly to the caller,
packaged_task stores the result in a future.
extern int some_function(); std::packaged_task<int> task(some_function); std::unique_future<int> result=task.get_future(); // later on, some thread does task(); // and "result" is now ready
promise to provide a future value
The other way to store a value to be picked up with a
shared_future is to use a
promise, and then explicitly set the value by calling the
set_value() member function.
std::promise<int> my_promise; std::unique_future<int> result=my_promise.get_future(); // later on, some thread does my_promise.set_value(42); // and "result" is now ready.
Futures also support storing exceptions: when you try and retrieve the value, if there is a stored exception, that exception is
thrown rather than the value being retrieved. With a
packaged_task, an exception gets stored if the wrapped function
throws an exception when it is invoked, and with a
promise, you can explicitly store an exception with the
set_exception() member function.
As the paper says, this is not a finished proposal: it is a basis for further discussion. Let me know if you have any comments.