23

I'm using boost::asio to do some very basic UDP packet collection. The io_service object is instantiated in a worker thread, and io_service.run() is called from inside that thread. My problem is getting io_service.run() to return when I am done collecting packets.

I'm not clear on what methods of io_service can be called from other threads when it comes time to stop my worker thread. I have a reference to the io_service object, and from a different thread I make this call:

ios.dispatch( boost::bind( &udp_server::handle_kill, this ) );

In my udp_server class, the handler for that function cancels the pending work from a single boost::asio::ip::udp::socket and a single boost::asio::deadline_timer object. Both have pending async work to do. At that point I call ios.stop():

void udp_server::handle_kill()
{
    m_socket.cancel();
    m_timer.cancel();
    m_ios.stop();
}

With no work pending, I expect at this point that my call to ios.run() should return - but this does not happen.

So why does it not return? The most likely explanation to me is that I shouldn't be calling io_service::dispatch() from another thread. But the dispatch() method kind of seems like it was built to do just that - dispatch a function call in the thread that io_service::run() is working in. And it seems to do just that.

So this leaves me with a few related questions:

  1. Am I using io_service::dispatch() correctly?
  2. If all tasks are canceled, is there any reason that io_service::run() should not return?
  3. socket::upd::cancel() doesn't seem to be the right way to close a socket and abort all work. What is the right way?

asio is behaving pretty well for me, but I need to get a better understanding of this bit of architecture.

More data

socket::udp::cancel() is apparently an unsupported operation on an open socket under Win32 - so this operation fails by throwing an exception - which does in fact cause an exit from io_service::run(), but definitely not the desired exit.

socket::udp::close() doesn't seem to cancel the pending async_receive_from() task, so calling it instead of socket::udp::cancel() seems to leave the thread somewhere inside io_service::run().

Community
  • 1
  • 1
Mark Nelson
  • 361
  • 1
  • 4
  • 7
  • Is there a reason you dispatch `handle_kill` to the `io_service`? It should be safe to call `handle_kill()` in that thread. If you do that, does it still hang in `run()`? – JaredC Jan 26 '11 at 19:27
  • this is a worker thread - it doesn't do much of anything but read packets. The impetus to kill the thread comes from the GUI, which is in another thread. Regardless of the method I use, there has to be a threadsafe way to communicate with the io_service::run() thread. It seems to me that io_service::dispatch() was built for just that need. And handle_kill() does seem to be called in the correct thread. – Mark Nelson Jan 26 '11 at 19:35
  • 6
    You should be able to stop the `io_service` from any thread, regardless of whether its a worker thread for that specific `io_service`. Also, `dispatch()` and `post()` are identical, with the exception that if possible, `dispatch()` may call the function inline immediately (if `dispatch` is called from an `io_service` thread). So, if you know for a fact that your `dispatch()` call will *never* be on an `io_service` thread, it is identical to `post()`. This doesn't solve your problem though. Is there a 10-15 line code sample you can give that reproduces the issue? – JaredC Jan 26 '11 at 19:40
  • I'm working on getting a short example that demonstrates this. In the meantime, I'm seeing that calling ios::stop() from another thread does in fact cause ios:run() to exit the way I want. I was (and still am) reluctant to make a call like this on a method that doesn't explicitly call out a guarantee of threadsafe behavior, but it does seem to work. The question of why my indirect call via dispatch() doesn't work remains to be reproduced and understood. – Mark Nelson Jan 26 '11 at 21:03
  • 2
    @MarkNelson, please accept the answer provided if it's adequate. – Brian Cain May 17 '12 at 02:51

1 Answers1

28

Invoking io_service::stop from another thread is safe, this is well described in the documentation

Thread Safety

Distinct objects: Safe.

Shared objects: Safe, with the exception that calling reset() while there are unfinished run(), run_one(), poll() or poll_one() calls results in undefined behaviour.

as the comments to your question indicate, you really need to boil this down to a reproducible example.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • I read the notes on thread safety, and I have to admit that I was a bit clueless about what the docs meant by "Shared Objects". It seems rather general, but in retrospect I think you are right, it is well described. This knowledge does not answer my question specifically, but since it clears the way for a workaround of my bug it is the next best thing. Thanks! – Mark Nelson Jan 26 '11 at 22:49
  • @Mark feel free to accept my answer to your question if you're satisfied. You might want to post a new question for your bug. – Sam Miller Jan 27 '11 at 02:20