0

My app (C++, Windows) is communicating with an external device. If the device doesn't answer after certain period of time, I want to reset a status variable.

My initial approach would be

auto timer = boost::asio::deadline_timer(io_svc);

timer.expires_from_now(boost::posix_time::seconds(10));
timer.async_wait(boost::bind(&Class::CurrRequestTimeout, this, boost::asio::placeholders::error));

io_svc.poll();

and the timeout function

void Class::CurrRequestTimeout(const boost::system::error_code & ec)
{
    if (ec)
    {
        // this timeout was canceled
        return;
    }
    ResetStatusVariable();
}

This should be non-blocking, that's why I selected poll() instead of run() (seen here). However, with poll() the timeout method is never called. With run() it works just fine, but this blocks the execution.

Community
  • 1
  • 1
Simon
  • 1,616
  • 2
  • 17
  • 39
  • Where is the code that's supposed to call the timeout method? If you didn't paste it, please do. If there is no such code, well there's your problem. – David Schwartz Nov 26 '12 at 10:19
  • async_wait() should call the timeout method after the timeout expired or it was canceled. At least that's my understanding of http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/reference/basic_deadline_timer/async_wait.html – Simon Nov 26 '12 at 10:25
  • 1
    If that were correct, how would it do it? There is no other thread, at least not that you've shown us. And `async_wait` can't call that function now, because it's not time yet. So how could that be right? – David Schwartz Nov 26 '12 at 10:27
  • I was assuming that the deadline_timer class would be some kind of abstraction layer, so I don't need to implement a thread by myself. Thanks for the clarification, so I will implement a thread by myself. – Simon Nov 26 '12 at 10:30
  • 1
    If it did that, then all code that used this would have to be thread-safe and all code would have to be able to cope with threads "coming out of nowhere". This way, it works perfectly even if your code is single-threaded. (Although I definitely agree it would be nice if it had a "create a few threads" function.) – David Schwartz Nov 26 '12 at 10:31

2 Answers2

2

The poll function only runs handlers that are ready to run at that instant. Since the timer hasn't timed out yet, it can't run that handler now. And you asked it not to block. So what would you expect it to do?

If your code is thread safe, create another thread that can block in run. If not, then this thread has to come back and call poll later to give handlers a chance to run.

One caveat: If you do create one or more run threads, you need to make sure there's always at least one event to wait for, or the thread will be unable to wait for a handler. boost::asio::io_service::work is there for just this purpose. See this question.

Community
  • 1
  • 1
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • I tried to derive my solution from that async example: http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/reference/deadline_timer.html#boost_asio.reference.deadline_timer.examples I think async_wait() runs that handler – Simon Nov 26 '12 at 10:22
  • 1
    No. The `async_wait` function can't run the handler because it returns immediately and the handler isn't ready to run yet. Whatever code runs the handler has to be some code that runs ten seconds from then. – David Schwartz Nov 26 '12 at 10:25
  • Found a solution in my code to put `run()` in a separate thread (which already existed). Thanks again for your clarifications. – Simon Nov 26 '12 at 14:44
0

If you call poll before the timeout, there is nothing to do (yet) for the io_service poll will return without any action. What do you want to do while you are waiting for the timeout or the communication from your external device? You could run a loop, waiting for the external devices communication, polling the io_service and checking for the timeout flag :

while (!checkForExternalDevicesCommunication())
{
  io_svc.poll();
  if (statusVariableHasTimeout())
    throw CommunicationTimeoutException();

  doSomethingInTheMeanTime();
  std::this_thread::sleep(some_time);
}

or implement the communication with your external device in an asynchronous fashion, Boost.Asio style and let it run on the same io_service.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • I was assuming that deadline_timer would already create a thread for me (see my comment to my question above). Thanks for clarification. – Simon Nov 26 '12 at 10:32
  • This copmpletely defeats the logic of the deadline timer. – David Schwartz Nov 26 '12 at 14:46
  • @DavidSchwartz care to elaborate? – Arne Mertz Nov 26 '12 at 14:53
  • The whole point of a deadline timer is so that you don't have to do this. What if he later has five timers? – David Schwartz Nov 26 '12 at 14:54
  • that one does not to do *what*? The loop I've written or the alternative I've mentioned (doing everything asio style)? Note that `io_service::poll()` will execute any handlers that are ready to execute, not just one. So if you have more than one timer so be it. Since the OP did not want a blocking call to run(), he sure wants to do something in the mean time and thus has to poll multiple times or create the thread to run() the io_service as he did. – Arne Mertz Nov 26 '12 at 15:04
  • I guess I see what you mean - my depicted loop is lacking the "do something in the mean time" part - i'll edit it. – Arne Mertz Nov 26 '12 at 15:18