Saturday, July 5, 2014

Async usage of futures and promises in ASIO

One of the new c++11 additions with great potential is futures and promises. However im a heavy asio based async programmer. Most exiting examples on the web deal directly with std::thread and handle the std::promise via std::move. But the asio design basically means you need to use the binding approach to coding. Unfortunately std::bind is great until u need to use a un-copyable move only class like std::promise.

Now you can do something crazy like rewrite the bind so that it can handle movable items, as i did in my post movable-stdbind-with-placeholders. Which is all well and good in theroy BUT there is something to be said about keeping to the standard and well used libs(like boost). Why? Well compiler developers and lib devs like the boost guys are working very hard to improve there stuff. So if you keep to the standard tools and libs you get all the bonuses from their hard work for free when u upgrade. Compiler designers research, profile and add better optimizations and well used libs get more and more eyes on, who in turn find and contribute better code.

And second to all that there is a really trivial and obvious solution to the problem: std::shared_ptr...

So here is how you use std::promise and std::future in lambdas, binds, asio and anything else u cant move into.. I also tossed in an example of how to use non-blocking checks on the futures which is another key tool for using futures in asio code.

#include <iostream>
#include <chrono>
#include <thread>
#include <future>
#include <boost/asio.hpp>

void asyncRun()
{
    std::cout << "Async..." << std::flush;

    boost::asio::io_service io_service;
    std::shared_ptr< std::promise<int> > promise(new std::promise<int>());
    std::future<int> future = promise->get_future();

    io_service.post(
                    [promise]()
                    {
                        std::chrono::milliseconds dura( 2000 );
                        std::this_thread::sleep_for( dura );
                        promise->set_value(9);
                    }
                    );

    std::thread t1( [&io_service]{ io_service.run(); });
    t1.detach();

    std::cout << "Waiting..." << std::flush;
    future.wait();
    std::cout << "Done!\nResults are: "
              << future.get() << '\n';

}


void nonBlockingRun()
{
    std::cout << "Non Blocking..." << std::flush;

    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    std::thread t1( [](std::promise<int> p)
                    {
                        std::chrono::milliseconds dura( 2000 );
                        std::this_thread::sleep_for( dura );
                        p.set_value(9);
                    },
                    std::move(promise) );
    t1.detach();

    std::cout << "Waiting...\n" << std::flush;
    std::future_status status;
    do {
        status = future.wait_for(std::chrono::seconds(0));

        if (status == std::future_status::deferred) {
            std::cout << "+";
        } else if (status == std::future_status::timeout) {
            std::cout << ".";
        }
    } while (status != std::future_status::ready);
    std::cout << "Done!\nResults are: "
              << future.get() << '\n';
}

void blockingRun()
{
    std::cout << "Blocking..." << std::flush;

    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    std::thread t1( [](std::promise<int> p)
                    {
                        std::chrono::milliseconds dura( 2000 );
                        std::this_thread::sleep_for( dura );
                        p.set_value(9);
                    },
                    std::move(promise) );
    t1.detach();

    std::cout << "Waiting..." << std::flush;
    future.wait();
    std::cout << "Done!\nResults are: "
              << future.get() << '\n';
}

int main()
{
    nonBlockingRun();
    blockingRun();
    asyncRun();
}

No comments:

Post a Comment