Flushing all asynchronous handlers in boost::asio
I'm running some tests that need asynchronous communication, and the underlying framework is Asio. Sometimes, a handler is kept in the processing loop even if the test has been tore down, for good reasons. But then, it is called after the targets are deleted.
The Test
class:
virtual void SetUp()
{
_client = new Client;
_server = new Server;
Service::run();
}
virtual void TearDown()
{
Service::stop();
delete _client;
delete _server;
}
The Service
class:
static void run()
{
_thread = new asio::thread(boost::bind(&asio::io_service::run, _service));
}
static void stop()
{
_service->stop();
_thread->join();
delete _thread;
_service->res开发者_JS百科et();
}
io_service::stop()
is non-blocking so it gets quite useless in my case. If I delete the io_service
object at the end of the function, the handler won't be called, but I'd like a better solution to force completion before the objects are deleted.
Note: the actual processing loop is done in a second thread, but it is joined in a io_service::stop()
wrapper, and the whole problem doesn't seem to be thread-related.
I'm using Asio (non-Boost) 1.4.5, but could consider upgrading (to get the io_service::stopped()
operation?).
EDIT: Add the io_service
wrapper code as it seems to be relevant according to the comments.
It appears to me you need to re-think your design slightly. io_service::stop
is indeed asynchronous, as you've noticed. It causes any invocations of io_service::run()
to return as soon as possible. Any outstanding handlers are not invoked with boost::asio::error::operation_aborted
until the ~io_service()
destructor runs. To manage object lifetimes, you should use a shared_ptr
as the documentation suggests:
The destruction sequence described above permits programs to simplify their resource management by using shared_ptr<>. Where an object's lifetime is tied to the lifetime of a connection (or some other sequence of asynchronous operations), a shared_ptr to the object would be bound into the handlers for all asynchronous operations associated with it. This works as follows:
- When a single connection ends, all associated asynchronous operations complete. The corresponding handler objects are destroyed, and all shared_ptr references to the objects are destroyed.
- To shut down the whole program, the io_service function stop() is called to terminate any run() calls as soon as possible. The io_service destructor defined above destroys all handlers, causing all shared_ptr references to all connection objects to be destroyed.
More specifically, you have a race condition
virtual void TearDown()
{
service->stop();
// ok, io_service is no longer running
// what if outstanding handlers use _client or _server ??
delete _client;
delete _server;
// now you have undefined behavior due to a dangling pointer
}
Have you considered using boost::shared_ptr to manage the object lifetimes? In some cases boost::enable_shared_from_this can be useful. An SO discussion can be found here.
Another option could be to join the thread after you call stop to make sure that the handlers have been called before deleting the objects.
精彩评论