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.

Reply via email to