Continuations using C++11

to_be_continued

PPL Task

While playing with the concurrency features in C++11, I noticed that there wasn’t any support for continuations.  As a learning exercise I decided to develop something similar to tasks in The Parallel Patterns Library (PPL), where a task  can run asynchronously and execute a continuation upon completion using the then  member function.

A great feature of the continuation above is that the signature for the continuation depends on the return value of the task .  The lambda above returns a double .  Therefore if the continuation doesn’t take a single double  or a task<double>  as a parameter the compiler will emit an error.

Future

Firstly in order to execute some code asynchronously I decided to use std::async  which generates a std::future  to hold the result of the asynchronous operation.  In order to be able to support multiple continuations I store a std::shared_future  rather than a std::future  allowing a  get()  call for each continuation.  If I didn’t do this an exception would be fired by attempting to call get()  multiple times on the future.  These implementation details don’t have any impact on the interface of the FutureWithContinuations  class however.  The class constructor has two template parameters.  One defines the callable and the other defines a parameter pack, allowing me to specify any number of arguments to pass through to the callable.

Continuation

I toyed with implementing the continuations using std::bind  to bind the callable to a parameter pack.  This would allow me to specify any number of arguments to pass to the continuation.  The first argument was going to be the return type of the std::future , which is why I used std::placeholders::_1  in the initialiser list for the continuation.

However this seemed a bit clunky just to support passing multiple arguments to the continuation.  In most cases the only argument that is of any interest for a continuation is the return value of the future.  I also read the chapter in Scott Meyer’s Effect Modern C++ book recommending the use of lambdas over std::bind .  The main arguments for using lambdas include better readability and potentially faster code.  Therefore I opted against using std::bind  and a parameter pack.

The next hurdle to overcome was the fact that the asynchronous operation might not return anything.  Therefore the continuation shouldn’t expect a parameter to be passed to it.  In order to achieve this I employed template specialisation and SFINAE.  The base version of the Continuation class defaults the third template parameter to void.

whereas the specialised version uses the type if the future return type isn’t void.  I do this by calling std::enable_if  and std::is_void  to check the future’s return type.

Note that this typename  can be simplified even further in C++14 by writing std::enable_if_t<!std::is_void_v<FutureReturnType>> .  The only difference between the two classes is that the execute function for the continuation, with a non-void future return type, passes the shared future as a parameter to the continuation function.

Summary

In order to validate my code I posted it on StackExchange’s code review site.  The only review I got was from a reviewer that didn’t fully understand the code, but at least they took the time to post their comments.  I also wrote tests as part of the validation.  In order to simulate a long running asynchronous operation to precede the continuation I used  std::this_thread::sleep_for(std::chrono::seconds(sleepDelay)); .  The sleep delay was set to two seconds.

Source Code

The source code can be found on Bitbucket.

Tagged , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *