Blocking cancel of pending async operations in Boost.Asio
开发者_如何学GoI have an object that receives callbacks from a boost::io_service
, and for a few reasons I cannot post the callback via a shared pointer (yes, I know it's the official way to handle it), so I bind the handler with a raw pointer. Assume it's a fixed requirement in this situation.
Now, if I delete the object, it will, of course, still receive a callback on an outstanding socket operation with "operation aborted" error code.
The question: is there a way to force a synchronous completion of all operations when I delete an object together with its owned Asio objects (sockets, timers)?
You can't; the information has been lost at that point. You can't even compare the function objects for equality, let alone peek inside and compare some pointer and then decide what to do.
So, the question is: why can't you use a shared pointer?
The way to do it is to use shared pointers and weak pointers. If you don't want to use shared pointers and weak pointers, you can implement the underlying mechanisms yourself. But it is generally more reliable just to use the library implementations.
So, use a weak pointer in the callback, have the callback take a weak_ptr as an argument, call wp.lock(), check it, and if it is still valid, then dereference it. There will still be a race condition where you clear the main shared_ptr and another thread calling wp.lock() (assuming you have multiple threads), but you can resolve this by using a flag in the object.
Update with response to comment:
Asio is not forcing you to use a shared_ptr/weak_ptr combination. You are free to build your own solution, but you have to address the same issues.
Assuming you can't use a weak_ptr, you should delete the object once you are sure nothing else will use the pointer. In principle you have two basic ways of doing this:
Detecting that the object has been deleted by using some additional data structure. This is what shared_ptr/weak_ptr do internally, and you are free to build your own equivalent.
Wait for everything to complete and then delete the object. No requirement to use shared_ptr/weak_ptr, but you need to do the book keeping somehow.
In these cases you end up keeping track of what's outstanding either by hand or using a library. The basic task is the same, but you're not forced to use a library. You are forced to solve that general problem.
The approach you are asking for, synchronously "cancelling" every outstanding operation so that you can safely delete an object, reduces to one of these cases.
Consider:
class Obj {
void queue() { wait_for_io(this, bind(&Obj::io_done, this, _1)); }
void io_done(error_code const& error)
{
// Do stuff.
}
};
void kill_object(Obj* o)
{
cancel_outstanding_operations_for_obj(o);
delete o;
}
What does the call to cancel_outstanding_operations() do if the call to Obj::io_done() is in progress in another thread? Does it wait for it to return, or does it return immediately because the I/O operation is complete? In the "return immediately" case, the "delete o" statement is not safe. In the "wait for it to return" case, you have the "wait for everything to complete" case above, except that you've added a bunch of implementation complexity and you have to do the wait synchronously.
精彩评论