Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do I tear down observer relationship in multithreaded C++?
    text
    copied!<p>I have a Subject which offers <code>Subscribe(Observer*)</code> and <code>Unsubscribe(Observer*)</code> to clients. Subject runs in its own thread (from which it calls <code>Notify()</code> on subscribed Observers) and a mutex protects its internal list of Observers.</p> <p>I would like client code - which I don't control - to be able to safely delete an Observer after it is unsubscribed. How can this be achieved?</p> <ul> <li>Holding the mutex - even a recursive mutex - while I notify observers isn't an option because of the deadlock risk.</li> <li>I could mark an observer for removal in the Unsubscribe call and remove it from the Subject thread. Then clients could wait for a special 'Safe to delete' notification. This looks safe, but is onerous for clients.</li> </ul> <p><strong>Edit</strong></p> <p>Some illustrative code follows. The problem is how to prevent Unsubscribe happening while Run is at the 'Problem here' comment. Then I could call back on a deleted object. Alternatively, if I hold the mutex throughout rather than making the copy, I can deadlock certain clients.</p> <pre><code>#include &lt;set&gt; #include &lt;functional&gt; #include &lt;boost/thread.hpp&gt; #include &lt;boost/bind.hpp&gt; using namespace std; using namespace boost; class Observer { public: void Notify() {} }; class Subject { public: Subject() : t(bind(&amp;Subject::Run, this)) { } void Subscribe(Observer* o) { mutex::scoped_lock l(m); observers.insert(o); } void Unsubscribe(Observer* o) { mutex::scoped_lock l(m); observers.erase(o); } void Run() { for (;;) { WaitForSomethingInterestingToHappen(); set&lt;Observer*&gt; notifyList; { mutex::scoped_lock l(m); notifyList = observers; } // Problem here for_each(notifyList.begin(), notifyList.end(), mem_fun(&amp;Observer::Notify)); } } private: set&lt;Observer*&gt; observers; thread t; mutex m; }; </code></pre> <p><strong>Edit</strong> </p> <p>I can't Notify observers while holding the mutex because of the deadlock risk. The most obvious way this can happen - the client calls Subscribe or Unsubscribe from inside Notify - is easily remedied by making the mutex recursive. More insidious is the risk of intermittent deadlock on different threads.</p> <p>I'm in a multithreaded environment, so at any point in a thread's execution, it will typically hold a sequence of locks L1, L2, ... Ln. Another thread will hold locks K1, K2, ... Km. A properly written client will ensure that different threads will always acquire locks in the same order. But when clients interact with my Subject's mutex - call it X - this strategy will be broken: Calls to Subscribe / Unsubscribe acquire locks in the order L1, L2, ... Ln, X. Calls to Notify from my Subject thread acquire locks in the order X, K1, K2, ... Km. If any of the Li or Kj can coincide down any call path, the client suffers an intermittent deadlock, with little prospect of debugging it. Since I don't control the client code, I can't do this.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload