Thursday, May 02, 2013

C# style async/await in C++

Asynchronous Programming

Asynchronous programming has become more and more important recently as a way to efficiently use the resources available with multicore processors yet at the same time avoid dealing with locking primitives.
In C++, two important libraries for this type of programming are Boost.Asio and Microsoft's Parallel Patterns Library (PPL) Task Library. Boost.Asio provides asynchronous operations with callback handlers. You can learn more about Boost.Asio here. The PPL Task Library provides asynchronous operations using continuations. You can learn more about PPL here.

Async/Await

The problem with using these libraries is that they operate differently from synchronous programming. Your logic ends up being in either multiple callback handlers or in multiple lambda continuations. C# recently added async/await to make it easier to write asynchronous code. You can find out more about them here and watch a presentation here.
There is even a proposal to add this to C++. You can see the proposal here. 

Async/Await using Boost.Coroutine

However, you don't want to wait for a language proposal to be approved and then get implemented my compilers to make your programming easier. In fact, you can have a lot of the benefit now. The key that you need is Boost.Coroutine. Boost.Coroutine is in the 1.53 release of Boost. You can read about Boost.Coroutine here. Using Boost.Coroutine, I wrote cpp_async_await which is an open source library with a Boost Software License that allows (as much as possible with a library only solution) async/await style programming in C++ with Boost.Asio and Microsoft PPL/PPLx.

Motivating example

Go take a look at a simple async http client using raw Boost.Asio http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/http/client/async_client.cpp


Welcome back. Boost.Asio is very powerful, but the callbacks make the logic hard to follow. In contrast here is our version of the same code. You can find the full code at


void get_http(boost::asio::io_service& io,std::string server, std::string path){

    using namespace asio_helper::handlers;
    // This allows us to do await
    asio_helper::do_async(io,[=,&io](asio_helper::async_helper helper){

        using boost::asio::ip::tcp;

        // This allows us to use the predefined handlers
        // such as read_handler, write_handler, etc
        using namespace asio_helper::handlers;

        tcp::resolver resolver_(io);
        tcp::socket socket_(io);
        boost::asio::streambuf request_;
        boost::asio::streambuf response_;

        // Form the request. We specify the "Connection: close" header so that the
        // server will close the socket after transmitting the response. This will
        // allow us to treat all data up until the EOF as the content.
        std::ostream request_stream(&request_);
        request_stream << "GET " << path << " HTTP/1.0\r\n";
        request_stream << "Host: " << server << "\r\n";
        request_stream << "Accept: */*\r\n";
        request_stream << "Connection: close\r\n\r\n";

QR: Inline image 1