6
votes

I'm using asio synchronous sockets to read data over TCP from a background thread. This is encapsulated in a "server" class.

However, I want the thread to exit when the destructor of this class is called. The problem is that a call to any of the read functions does block, so the thread cannot be easily terminated. In Win32 there is an API for that: WaitForMultipleObjects which would do exactly what I want.

How would I achieve a similar effect with boost?

5

5 Answers

2
votes

In our application, we set the "terminating" condition, and then use a self-connection to the port that the thread is listening on so it wakes up, notes the terminate condition and terminate.

You could also check the boost implementation - if they are only doing a plain read on the socket (i.e., not using something like WaitForMultipleObjects internally themselves) then you can probably conclude that there isn't anything to simply and cleanly unblock the thread. If they are waiting on multiple objects (or a completion port) you could dig around to see if the ability to wake blocking thread is exposed to the outside.

Finally, you could kill the thread - but you'll have to go outside of boost to do this, and understand the consequences, such as dangling or leaked resources. If you are shutting down, this may not be a concern, depending on what else that thread was doing.

2
votes

I have found no easy way to do this. Supposedly, there are ways to cancel win32 IOCP, but it doesn't work well on windows XP. MS did fix it for windows vista and 7. The recommended approach to cancel asio async_read or async_write is to close the socket.

  • [destructor] note that we want to teardown
  • [destructor] close the socket
  • [destructor] wait for completion handlers

  • [completion] if tearing down and we just failed because the socket closed, notify the destructor that the completion handlers are done.

  • [completion] return immediately.

Be careful if you choose to implement this. Closing the socket is pretty straight forward. 'wait for completion handlers' however is huge understatment. There are several subtle corner cases and race conditions that could occur when the server's thread and its destructor interact.

This was subtle enough that we build a completion wrapper (similar to io_service::strand just to handle synchronously canceling all pending completion callbacks.

1
votes

Best way is to create a socketpair(), (whatever that is in boost::asio parlance), add the reader end to the event loop, then shut the writer end down. You'll be woken up immediately with an eof event on that socket.

The thread must then voluntarily shut itself down.

The spawner of the thread should in its destructor, have the following:

~object()
{
    shutdown_queue.shutdown();   // ask thread to shut down
    thread.join();               // wait until it does
}
0
votes
boost::system::error_code _error_code;
client_socket_->shutdown(client_socket_->shutdown_both, _error_code);

Above code help me close sync read immediately.

-1
votes

Use socket.cancel(); to end all current asynchronous operations that are blocking on a socket. Client sockets might need to be killed in a loop. I've never had to shut the server down this way, but you can use shared_from_this() and run cancel()/close() in a loop similarly to how the boost chat example async_writes to all client.