c++ - Safely disconnecting from boost::signals2 -
with boost signals (which deprecated) wrapped connection management , signal invocation mutex in order thread-safe. boost signals 2 should give out-of-the-box. but:
according boost signals2 thread-safety documentation, possible disconnect slots thread while executing on thread b. let's assume created object o on thread , connected fellow member function of o signal s executed on worker thread b. now, reasons o needs destroyed , disconnected s before. here example:
#include <iostream> #include <boost/thread.hpp> #include <boost/signals2.hpp> #include <boost/bind.hpp> using namespace std; using namespace boost; struct object { object() : x(0) {cout << __pretty_function__ << endl;} virtual ~object() {cout << __pretty_function__ << endl;} void dosomething() { this_thread::sleep(posix_time::milliseconds(4200)); cout << "accessing fellow member in " << __pretty_function__ << endl; x++; } int x; }; class worker { public: worker() {} virtual ~worker() {mythread.join();} void start() { mythread = thread(bind(&worker::dothread, this)); } signals2::signal<void ()> signal; private: void dothread() { // thread b signal(); } thread mythread; }; int main(int argc, char* argv[]) { worker w; { // thread object o; signals2::connection bc = w.signal.connect(bind(&object::dosomething, &o)); w.start(); this_thread::sleep(posix_time::seconds(2)); bc.disconnect(); } homecoming 0; }
executing code prints:
object::object() virtual object::~object() accessing fellow member in void object::dosomething()
as can see, i'm accessing destroyed object. so, ended wrapping signal mutex again.
connection worker::connect(..) { mutex::scoped_lock l(_mutex); signal.connect(..); } void worker::disconnect(connection c) { mutex::scoped_lock l(_mutex); c.disconnect(); } void worker::raise() { mutex::scoped_lock l(_mutex); signal(); }
am missing something? there easier way safely disconnect boost signals 2?
i think problem object.
the object not threadsafe, however, seem simultaneously executing fellow member function (the signal handler) , it's destructor.
the solution, then, remove race condition. either
lock destruction of class until it's "idle" bind signal handler instance usingboost::shared_ptr
/boost::shared_from_this
. way, don't have explicitly manage lifetime @ all, , destructor "magically" run after signal handler (assuming got disconnected in mean time), because that's when lastly reference bind-expression[1] goes out of scope. here's mean live on coliru, printing:
object::object() accessing fellow member in void object::dosomething() virtual object::~object()
full listing #include <iostream> #include <boost/enable_shared_from_this.hpp> #include <boost/make_shared.hpp> #include <boost/thread.hpp> #include <boost/signals2.hpp> #include <boost/bind.hpp> using namespace std; using namespace boost; struct object : boost::enable_shared_from_this<object> { object() : x(0) {cout << __pretty_function__ << endl;} virtual ~object() {cout << __pretty_function__ << endl;} void dosomething() { this_thread::sleep(posix_time::milliseconds(4200)); cout << "accessing fellow member in " << __pretty_function__ << endl; x++; } int x; }; class worker { public: worker() {} virtual ~worker() {mythread.join();} void start() { mythread = thread(bind(&worker::dothread, this)); } signals2::signal<void ()> signal; private: void dothread() { // thread b signal(); } thread mythread; }; int main() { worker w; { // thread auto o = boost::make_shared<object>(); signals2::connection bc = w.signal.connect(bind(&object::dosomething, o)); w.start(); this_thread::sleep(posix_time::seconds(2)); bc.disconnect(); } homecoming 0; }
[1] or c++11 lambda, of course
c++ multithreading boost boost-signals2
No comments:
Post a Comment