3

Consider a blocking function: this_thread::sleep_for(milliseconds(3000));

I'm trying to get the following behavior:

Trigger Blocking Function               

|---------------------------------------------X

I want to trigger the blocking function and if it takes too long (more than two seconds), it should timeout.

I've done the following:

my_connection = observable<>::create<int>([](subscriber<int> s) {
    auto s2 = observable<>::just(1, observe_on_new_thread()) |
    subscribe<int>([&](auto x) {
        this_thread::sleep_for(milliseconds(3000));
        s.on_next(1);
    });
}) |
timeout(seconds(2), observe_on_new_thread());

I can't get this to work. For starters, I think s can't on_next from a different thread.

So my question is, what is the correct reactive way of doing this? How can I wrap a blocking function in rxcpp and add a timeout to it?

Subsequently, I want to get an RX stream that behaves like this:

Trigger                Cleanup

|------------------------X
                           (Delay)   Trigger           Cleanup
                                       |-----------------X
jc211
  • 197
  • 1
  • 8
  • Another way I thought this can be done is: `auto connection = timeout.amb(blocking_function_observable)` – jc211 Jul 13 '17 at 00:50

1 Answers1

2

Great question! The above is pretty close.

Here is an example of how to adapt blocking operations to rxcpp. It does libcurl polling to make http requests.

The following should do what you intended.

auto sharedThreads = observe_on_event_loop();

auto my_connection = observable<>::create<int>([](subscriber<int> s) {
        this_thread::sleep_for(milliseconds(3000));
        s.on_next(1);
        s.on_completed();
    }) |
    subscribe_on(observe_on_new_thread()) |
    //start_with(0) | // workaround bug in timeout
    timeout(seconds(2), sharedThreads);
    //skip(1); // workaround bug in timeout

my_connection.as_blocking().subscribe(
    [](int){}, 
    [](exception_ptr ep){cout << "timed out" << endl;}
);
  • subscribe_on will run the create on a dedicated thread, and thus create is allowed to block that thread.
  • timeout will run the timer on a different thread, that can be shared with others, and transfer all the on_next/on_error/on_completed calls to that same thread.
  • as_blocking will make sure that subscribe does not return until it has completed. This is only used to prevent main() from exiting - most often in test or example programs.

EDIT: added workaround for bug in timeout. At the moment, it does not schedule the first timeout until the first value arrives.

EDIT-2: timeout bug has been fixed, the workaround is not needed anymore.

Kirk Shoop
  • 1,154
  • 7
  • 12
  • Hi Kirk, thanks for your input on this. I ran the code snippet you suggested. It seems the `on_next` is triggering before the `on_error`. My suspicion is the `this_thread::sleep_for()` is blocking the timeout from starting the internal timer. If the `s.on_completed` is removed, an `on_next` fires followed by `on_error` from the timeout. – jc211 Jul 13 '17 at 00:49
  • that is what I get for not trying this first. I found a bug in the timeout operator, I have updated the answer with a workaround for the bug. – Kirk Shoop Jul 13 '17 at 04:55
  • 1
    I merged a PR that fixes `timeout`. The workaround is not needed anymore! Keeping the workaround will have some minor overhead, but otherwise will still work the same with the fixed `timeout` – Kirk Shoop Jul 15 '17 at 16:21