How can I cancel already posted callback:
getIoService()->post(boost::bind(&MyClass::myCallback, this));
and keep other posted callbacks untouched?
The problem is that I have some object that receives events from different thread and I post them to ioservice in order to handle events in main thread. What if at some point I want to delete my object - ioservice will try to execute already posted callbacks in destroyed object. And in this case I can't store any flag in object since it will be removed.
There is a possible solution to use enable_shared_from_this
and shared_from_this()
, but wondering whether another solution or not.
Thanks
You cannot selectively cancel callbacks in such a manner through an io_service
. One option is to move the logic to a higher level, such as inside of MyClass
. A sample implementation may be:
class MyClass : public boost::enable_shared_from_this<MyClass>
{
public:
typedef boost::shared_ptr<MyClas> Ptr;
static Ptr create( boost::asio::io_service& io_service ) {
const Ptr result( new MyClass );
io_service.post( boost::bind(&MyClass::myCallback, result) );
return result;
}
void myCallback() {
if ( _canceled ) return;
}
void cancel() { _canceled = true; }
private:
MyClass() : _canceled(false) { }
private:
bool _canceled;
};
This class uses a boost::shared_ptr
to enforce shared ownership semantics. Doing this gurantees the object lifetime will persist as long as the callback remains in the io_service
queue before being dispatched.
As Sam answered, it is not possible to selectively cancel posted handlers.
If the goal is to prevent calling a member function on an object whose lifetime has expired, then using enable_shared_from_this
is the idiomatic solution. One consequence of this approach is that the lifetime of the object is extended to be at least that of the handler. If the object's destructor can be deferred, then consider binding the object to a handler via shared_from_this()
.
On the other hand, if destruction needs to be immediate, then consider writing a functor that weakly binds to the instance. This question discusses binding to a weak_ptr
, and provides some research/discussion links. Here is a simplified complete example of a functor that weakly binds to an object:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
/// @brief Mocked up type.
class MyClass:
public boost::enable_shared_from_this<MyClass>
{
public:
MyClass() { std::cout << "MyClass()" << std::endl; }
~MyClass() { std::cout << "~MyClass()" << std::endl; }
void action() { std::cout << "MyClass::action()" << std::endl; }
};
/// @brief weak_binder is a functor that binds a member function
/// to a weakly managed object instance. I.e. this
/// functor will not extend the life of the instance to
/// which it has been bound.
template <typename Fn,
typename C>
struct weak_binder
{
private:
typedef typename C::element_type element_type;
public:
/// @brief Constructor.
weak_binder(Fn& fn, C& c) : fn_(fn), c_(c)
{}
/// @brief Conditional invoke Fn if C still exists.
void operator()()
{
std::cout << "weak_binder::operator()" << std::endl;
// Create a shared pointer from the weak pointer. If
// succesful, then the object is still alive.
if (boost::shared_ptr<element_type> ptr = c_.lock())
{
// Invoke the function on the object.
(*ptr.*fn_)();
}
}
private:
Fn fn_;
boost::weak_ptr<element_type> c_;
};
/// @brief Helper function to create a functor that weakly
/// binds to a shared object.
template <typename Fn,
typename C>
weak_binder<Fn, C> weak_bind(Fn fn, C c)
{
return weak_binder<Fn, C>(fn, c);
}
int main()
{
boost::asio::io_service io_service;
boost::shared_ptr<MyClass> my_class = boost::make_shared<MyClass>();
// my_class will remain alive for this handler because a shared_ptr
// is bound to handler B, and handler B will only be destroyed after
// handler A has been destroyed.
io_service.post(weak_bind(&MyClass::action,
my_class->shared_from_this())); // A
// my_class will remain alive for this handler because it is bound
// via a shared_ptr.
io_service.post(boost::bind(&MyClass::action,
my_class->shared_from_this())); // B
// my_class will not be alive for this handler, because B will have
// been destroyed, and the my_class is reset before invoking the
// io_service.
io_service.post(weak_bind(&MyClass::action,
my_class->shared_from_this())); // C
// Reset the shared_ptr, resulting in the only remaining shared_ptr
// instance for my_class residing within handler B.
my_class.reset();
io_service.run();
}
And the resulting output:
MyClass()
weak_binder::operator()
MyClass::action()
MyClass::action()
~MyClass()
weak_binder::operator()
As can be observed, MyClass::action()
is only invoked twice: once through weak_binder
while the instance was alive (handler A), and once through the boost::bind
where the instance is maintained via a shared_ptr
(handler B). Handler C is invoked, but weak_binder::operator()
detects that the instance has been destroyed, resulting in a silent no-op.