On Wed, May 04, 2005 at 02:47:14PM -0600, Chris Friesen wrote: > In multiple messages to comp.programming.threads he has stated that > volatile is not necessary between threads if you use the posix locking > functions, and in fact that one of the main purposes of the posix locks > is to ensure correct memory visibility between threads. > That still assumes that the compiler isn't pulling the body of pthread_mutex_lock, deciding that it doesn't really clobber any globals and proceeding to consider it a read-only function. At this point in time, and for the foreseeable future this will remain to be true.
For instance, ----------------------------------------------------------------------------- #include <pthread.h> static int shared_var; foo () { int *p = &shared_var; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock (&lock); *p += 3; pthread_mutex_unlock (&lock); return *p; } ----------------------------------------------------------------------------- roughly translates to this IL after alias analysis (i've removed some chunks for clarity): ----------------------------------------------------------------------------- 1 p_1 = &shared_var; 2 3 # shared_var_10 = V_MAY_DEF <shared_var_9>; 4 # lock_11 = V_MAY_DEF <lock_4>; 5 pthread_mutex_lock (&lock); 6 7 # VUSE <shared_var_10>; 8 D.1668_5 = *p_1; 9 D.1669_6 = D.1668_5 + 3; 10 11 # shared_var_12 = V_MAY_DEF <shared_var_10>; 12 *p_1 = D.1669_6; 13 14 # shared_var_13 = V_MAY_DEF <shared_var_12>; 15 # lock_14 = V_MAY_DEF <lock_11>; 16 pthread_mutex_unlock (&lock); 17 18 # VUSE <shared_var_13>; 19 D.1670_7 = *p_1; 20 21 return D.1670_7; ----------------------------------------------------------------------------- See the calls to pthread_mutex_lock and pthread_mutex_unlock? Those V_MAY_DEF operators you see above them are there to indicate that those calls may clobber those variables. So, lines 3 and 4 indicate that the compiler thinks that pthread_mutex_lock will clobber shared_var and lock. Similarly with lines 14 and 15 and pthread_mutex_unlock. Now, suppose that the compiler grows enough capabilities to pull in the bodies of pthread_mutex_lock/pthread_mutex_unlock into the current compilation unit. It examines them and determines that neither function really writes to shared_var, so you end up with: ----------------------------------------------------------------------------- 1 [ ... ] 2 # shared_var_12 = V_MAY_DEF <shared_var_10>; 3 *p_1 = D.1669_6; 4 5 # lock_14 = V_MAY_DEF <lock_11>; 6 pthread_mutex_unlock (&lock); 7 8 # VUSE <shared_var_12>; 9 D.1670_7 = *p_1; 10 [ ... ] ----------------------------------------------------------------------------- Notice how the load from *p_1 at line 8 is reading from shared_var_12, which is the very same shared_var_12 stored to in line 2. At that point, you've lost. The compiler will happily move D.1669_6 into line 9. Since GCC is a sequential compiler and we haven't told it anything about the semantics of pthread_mutex_lock (in fact, I don't see pthread.h marking them in any particular way), it doesn't know that they're memory flushing sites. The real future-proof fix is to mark them as such and have the compiler recognize that attribute. This means adding concurrency semantics to the compiler. Something that we are in the process of doing. Diego.