0

In c++11 how would one go about implementing a program that does two expensive (network) calls, of the same type, and then only waits for the result from the quicker one, not waiting and discarding the slower result. std::thread cant be interrupted and does not return a convenient std::future. And std::async that returns a future, can neither be interrupted nor detached.

The two main issues are: -Notification when the quicker result has arrived. -Terminating (and cleanup up) of the slower thread.

petke
  • 123
  • 1
  • 7
  • 2
    use nonblocking sockets and select() – Markus Mikkolainen Oct 04 '12 at 15:59
  • (answer depends on your development environment and platform) – Markus Mikkolainen Oct 04 '12 at 15:59
  • Thanks. I was hoping for a more generic c++ based solution though. What if the call is not a network call, or the network library API does not support early termination? Ideally would like the slower thread run to completion but ignoring it somehow. – petke Oct 04 '12 at 16:05
  • 1
    Definitely non-blocking file descriptors. You can just close both once you receive the first result. – Kerrek SB Oct 04 '12 at 16:08
  • 1
    "Interrupting" a program is never natural. The solution depends on the specific details, and you have to design a program so that it can be interrupted meaningfully. – Kerrek SB Oct 04 '12 at 16:11
  • 3
    for fun you might wanna try to check out : http://fpcomplete.com/functional-patterns-in-c/ – NoSenseEtAl Oct 04 '12 at 16:43
  • @NoSenseEtAl Thanks for the link. In the video Bartoz is talking about an "OR combinator" of async functors. That's Exactly what I'm looking for. Hopefully at the end of the video there is a c++11 implementation. This guy should write a book. – petke Oct 04 '12 at 17:45
  • 1
    Threads are for _parallel_ programming; for _asynchronous_ programming, you should be using something like [Boost.ASIO](http://www.boost.org/libs/asio/). – ildjarn Oct 04 '12 at 18:53

2 Answers2

1

NoSenseEtAl provided a link to:

http://fpcomplete.com/functional-patterns-in-c/

There Bartosz provides example code of some very generic composable async API's. I shamelessly stripped it down to only what I needed (and could understand). Just the "Async Or-Combinator":

#include <functional>
#include <iostream>
#include <string>

#include <memory>
#include <algorithm>
#include <ctype.h>

#include <thread>
#include <mutex>
#include <chrono>

#include <random>

using namespace std;

//--------
// Helpers
//--------

void tick(int n)
{
    for(int i = 0; i < n; ++i)
    {
        cout << i << endl;
        this_thread::sleep_for(chrono::seconds(1));
    }
}

//-------
// Async
//-------

template<class A>
struct Async {
    virtual ~Async() {}
    virtual void andThen(function<void(A)>) = 0;
};

//-------
// Monoid
//-------

template<class A>
struct Mplus : Async<A>
{
    Mplus(unique_ptr<Async<A>> asnc1, unique_ptr<Async<A>> asnc2) 
    : _asnc1(move(asnc1)), _asnc2(move(asnc2)), _done(false)
    {}
    ~Mplus() {}
    void andThen(function<void(A)> k)
    {
        _asnc1->andThen([this, k](A a)
        {
            lock_guard<mutex> l(_mtx);
            if (!_done)
            {
                _done = true;
                k(a);
            }
        });
        _asnc2->andThen([this, k](A a)
        {
            lock_guard<mutex> l(_mtx);
            if (!_done)
            {
                _done = true;
                k(a);
            }
        });
    }
    unique_ptr<Async<A>> _asnc1;
    unique_ptr<Async<A>> _asnc2;
    bool _done;
    mutex _mtx;
};

template<class A>
unique_ptr<Async<A>> mplus(unique_ptr<Async<A>> asnc1, unique_ptr<Async<A>> asnc2)
{
    return unique_ptr<Async<A>>(new Mplus<A>(move(asnc1), move(asnc2)));
}

//----------------
// Fake async APIs
//----------------

void getStringAsync(string s, function<void(string)> handler)
{
    thread th([s, handler]()
    {
        cout << "Started async\n";

        size_t sleep = rand () % 10;
        this_thread::sleep_for(chrono::seconds(sleep));

        handler("Done async: " + s);
    });
    th.detach();
}

struct AsyncString : Async<string>
{
    AsyncString(string s) : _s(s) {}
    void andThen(function<void(string)> k)
    {
        getStringAsync(_s, k);
    }
    string _s;
};

unique_ptr<Async<string>> asyncString(string s)
{
    return unique_ptr<Async<string>>(new AsyncString(s));
}

void testOr()
{
    // Test or combinator / mplus
    auto or = mplus<string>(asyncString(" Result One "), asyncString(" Result Two "));
    or->andThen([](string s)
    {
        cout << "Or returned : " << s << endl;
    });

    tick(10);
}

void main()
{
    srand ( time(NULL) );

    testOr();
}
petke
  • 123
  • 1
  • 7
  • Are you sure this is an improvement on a well-tested, well-documented library already used by thousands of people (such as the aforementioned Boost.ASIO)? – ildjarn Oct 04 '12 at 20:37
  • @ildjarn boost::asio seem good, but big. I'm just afraid of the time it will take to learn, especially if I need to extend it to actually solve my specific problem. Does it have something similar to this type of "Async Or-Combinator"? – petke Oct 04 '12 at 20:55
  • 1
    ASIO is an implementation of the [proactor pattern](http://en.wikipedia.org/wiki/Proactor_pattern). Some good slides from BoostCon 2010 that show the high-level design of the pattern/library can be found here: https://dl.dropbox.com/u/10282384/asio_presentation_with_story.pdf – ildjarn Oct 04 '12 at 20:58
  • @ildjarn I had a very quick look at asio. The networking interfaces worry me a bit. I already have a networking library (curl), I'm just looking for a minimal but generic functor async API. Looking at the examples, I don't see exactly what I'm looking for. I suspect there would be a fair amount of boilerplate that needs to be written to support this type of a async Or-Combinator. Also I'm not sure how Asio works with regard to std::threads and std::async. Maybe I'll have another look later. – petke Oct 04 '12 at 21:40
  • @ildjarn What can be used from ASIO to have the OR combinator ? – Ghita Oct 05 '12 at 08:05
  • 1
    Im glad you found the link interesting. I stopped at the beginning of video 3 because Im in a rush recently. :) BTW Please update your answer with results and experiences if you end up using the code. – NoSenseEtAl Oct 05 '12 at 08:36
  • Great to see this example here. I saw the mentioned presentations on youtube but seeing examples is great. – Ghita Oct 05 '12 at 09:17
  • 1
    @Ghita : Multiple `async_*`s (e.g. `async_read`s) with the same handler, wherein the handler cancels all active operations other than the first to be handled. (Atomics and `shared_ptr` make this trivial.) – ildjarn Oct 05 '12 at 19:36
  • @petke : You don't need to use ASIO's networking components; ASIO's _core_ components are all you need, and you can supply external data via `buffered_stream`. – ildjarn Oct 05 '12 at 19:38
  • @ildjarn Too bad then none of it it's documented :-) – Ghita Oct 05 '12 at 20:30
  • @Ghita : It very much _is_ documented. ASIO comes with a plethora of tutorials. – ildjarn Oct 05 '12 at 20:31
  • @ildjarn Then probably I have to be one of the few people to miss this. You could add your answer here also : http://stackoverflow.com/questions/244453/best-documentation-for-boostasio – Ghita Oct 05 '12 at 20:33
0

You have to use the facilities provided by the operating system to issue "truly" async operations that send some form of notification when they complete. Often such operations can be terminated before completion; if they can't you'll have to find another mechanism, or simply let them complete and ignore the result.

Christian Stieber
  • 9,954
  • 24
  • 23