[thread] memory synchronization multiple mutexes
Hi, How does memory synchronization and visibility behave when multiple mutexes are involved? I have two groups of threads (boost::thread_group) and two mutexes (boost::mutex m1, boost::mutex m2) + associated condition variables, with each thread group being responsible to write to some variables (here Ptr, run) and consistently using "its" mutex for locking. However there is a piece of code that looks - much simplified - like this: void f() { { boost::unique_lockboost::mutex Lock(m1); Ptr = someAddress; // set the Ptr from 0 to some memory address } { boost::unique_lockboost::mutex Lock(m2); run = 1; // was 0, inform that execution can proceed condVar2.notify_all(); } ... // wait until Ptr has been reset to 0 { boost::unique_lockboost::mutex Lock(m1); while (Ptr != 0) condVar1.wait(Lock); } } and another function: void g() { { boost::unique_lockboost::mutex Lock(m2); while (run == 0) condVar2.wait(Lock); } // run the execution, e.g. std::copy(Ptr, Ptr + 100, Target); // finally all finished, signal back that Ptr is not needed any more { boost::unique_lockboost::mutex Lock(m1); Ptr = 0; condVar1.notify_all(); } } f() is only invoked from a thread of the first group, and g() only from a thread of the second group. Whenever Ptr might get changes the first mutex (m1) and its associated condition-variable is used, and whenver run might get changed the second mutex and its associated conditional variable is used. The control flow guarantees that run is not set to 1 prior to Ptr being set, and thus in g() the "running" itself (here the std::copy) cannot commence earlier. However, what puzzles me now: I used a different mutex for setting Ptr and setting/checking run. Does this have any impact whatsoever on memory visibility across the threads in the two thread-groups? Specifically, is it always guaranteed that when g() continues its execution (because run == 1) that it must see the correct value of Ptr (a non-NULL value)? Or is it possible that it still sees 0 and I'd have to make a separate locking involving m1 to guarantee a proper read for Ptr as anything written under a lock of m1 is also only guaranteed to be later properly read if the locking also used m1? thanks!
On 29/08/2014 00:53, firespot wrote:
How does memory synchronization and visibility behave when multiple mutexes are involved?
It's typically safe to assume that both locking and unlocking generate a compiler fence, so the compiler is not allowed to move reads or writes from after a mutex acquire to before it, or from before a mutex release to after it. In practice it shouldn't move code outside of the guarded block to inside it, either. This is irrespective of what actual mutexes are involved, as the compiler and processor are not aware of the relationship between mutexes and their protected variables -- that's a conceptual thing only.
{ boost::unique_lockboost::mutex Lock(m1); Ptr = someAddress; // set the Ptr from 0 to some memory address }
Provided that exactly one of each of f() and g() can be run concurrently (and there's no other code that messes with these variables), then this lock is actually unnecessary. g() will block on m2 waiting for run to be set, and so there is no possible race on Ptr in this case (assuming that run is always initially false). Things do get hairier (and the code you provided will get in trouble) if it is possible for there to be more than one instance of either function running concurrently on the same variables.
participants (2)
-
firespot
-
Gavin Lambert