A std::promise means nothing until it’s delivered.

I have really enjoyed reading Anthony Williams’s C++ Concurrency in Action and highly recommended it if you want to get up to speed on the new threading support provided in the C++11 standard.

However, I did run into trouble on my first reading when, after the explanations of std::thread and the use of join() or detach(), std::async was introduced as a means to return a value back from a thread. Briefly, this is achieved by std::async returning a std::future object from which the return value from the thread can be retrieved (or an exception rethrown if one has been raised during the running of the asynchronous thread).

All that was OK but in quick succession std::packaged_task and std::promise are introduced. There are some examples, but this was certainly the point I was beginning to feel I was no longer really ‘getting it’. I continued reading with a note to revisit this section later.

Fast forward a while and two sources of information I happened upon led me to the “aha!” moment I was looking for with this subject.

The first was when I got the latest edition of the Josuttis’s classic The Standard C++ Library. This has been updated for all the new C++11 library features and also now includes a chapter on concurrency. This takes a different approach to explaining the creation of threads by instead starting with std::async and presenting it as a high-level convenience function to create a thread and retrieve a value. Only then does it then goes onto explain std::thread and std::promise. So, the key point I had missed from C++ Concurrency In Action on the first reading is that the std::async functionality is actually built using std::thread, std::promise and std::future.

This was further reinforced when I found a code fragment on Bartosz Milewski’s blog showing the use of std::promise. This inspired me to put together the following code for a basic std::async implementation in order to understand what this could be doing ‘under the hood’ and to help me get a better understanding of the interaction of these ‘building blocks’.

Please Note! This is only intended to be a very simplistic version of async as, for example, it only accepts a single parameter for the function to be run asynchronously and does not allow any launch policy to be specified. It is presented to highlight the relationship between std::promise and std::future, and the fact that std::async hides the promise part of this interaction. In fact, this is not the only way to achieve this as it could also be implemented as a std::packaged_task, but that’s another story.

template<typename Function, typename Arg>
std::future<typename std::result_of<Function(Arg)>::type>
SimpleAsync(Function func, Arg arg)
{
    typedef std::result_of<Function(Arg)>::type return_val;

    std::promise<return_val> prom;
    std::future<return_val> fut = prom.get_future();

    std::thread thr([](std::promise<return_val>& prom, Function func, Arg arg)
    {
        try
        {
            prom.set_value(func(arg));
        }
        catch (const std::exception& e)
        {
            prom.set_exception(std::current_exception());
        }
    }, std::move(prom), func, arg);

    thr.detach();

    return std::move(fut);
}

Although the std::promise is hidden from users by being created in the SimpleAsync function (in order to be able to retrieve the corresponding std::future object for the return value), it is actually moved into the lambda in order to ensure its lifetime matches that of the asynchronous thread.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: