tags:

views:

82

answers:

3

Consider a simple (global in my case) variable:

int i;

Somewhere this variable is accessed

pthread_mutex_lock(i_mutex);
if(i == other value) {
  do_something();
}
pthread_mutex_unlock(i_mutex);

Another thread updates i while it holds i_mutex . Could the compiler cache the value of i so I don't get the recent value ? Must i be volatile ?

+4  A: 

pthread locks implement memory barriers that will ensure that cache effects are made visible to other threads. You don't need volatile to properly deal with the shared variable i if the accesses to the shared variable are protected by pthread mutexes.

from http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11:

The following functions synchronize memory with respect to other threads:

fork()
pthread_barrier_wait()
pthread_cond_broadcast()
pthread_cond_signal()
pthread_cond_timedwait()
pthread_cond_wait()
pthread_create()
pthread_join()
pthread_mutex_lock()       // <====
pthread_mutex_timedlock()
pthread_mutex_trylock()
pthread_mutex_unlock()     // <====
pthread_spin_lock()
pthread_spin_trylock()
pthread_spin_unlock()
pthread_rwlock_rdlock()
pthread_rwlock_timedrdlock()
pthread_rwlock_timedwrlock()
pthread_rwlock_tryrdlock()
pthread_rwlock_trywrlock()
pthread_rwlock_unlock()
pthread_rwlock_wrlock()
sem_post()
sem_timedwait()
sem_trywait()
sem_wait()
semctl()
semop()
wait()
waitpid()
Michael Burr
So, basically, a `for(;;) { int k; pthread_mutex_lock(i_mutex); k = i; if(k == other_value) do_something(); pthread_mutex_unlock(i_mutex); ` , the `i` would always be read from memory and never optimized into being kept in e.g. a register, just because the precence of pthread_mutex_XX ?
Anonym
@Anonym: No, it's not because of pthread_mutex_XX, it's because of the function call (a sequence point in C). See Jens Gustedt's answer. The memory barrier doesn't protect against caching, it protects against reordering (it doesn't let accesses cross the barrier).
ninjalj
@ninjalj: The function call sequence point *combined* with the memory barrier provided by the pthreads functions ensure that the consistent `i` value will be used. The sequence point alone is not enough because without the memory barrier cache effects might read a stale value of `i` even if the compiler inserts a memory read after the function call (for example if some non-pthread function were called).
Michael Burr
got it, the compiler assumes any of those function calls could modify `i` , and will have to re-read it - volatile or not, while the pthread_mutex_xxx ensures the low level consistency.
Anonym
@Anonym: Almost. The function call avoids caching and acts as a compiler barrier (prevents the compiler reordering the access acros the compiler barrier). Possibly a CPU barrier prevents the CPU from reordering the access across the CPU barrier. And the mutex prevents simultaneous access to the protected data. Or so goes my understanding. For the fine details, you definitely need a wizard, a simple guru won't do ;P
ninjalj
+1  A: 

A compiler should not cache such a global value across a function call.

But I think your question is ill-posed. First, POSIX mutex only work when you stick to their semantics. So you have to apply some discipline in your code to only access global variables (i in that case) whence your mutex is hold.

Second, please don't think that volatile declarations would prevent you from any damage that such a non-respect of the access rules would cause. Concurrent read and write into memory is a subtle subject.

Jens Gustedt
This is correct, but the "discipline" you mention is simply that you must lock the mutex whenever your read *or* write the data you are interested in.
Randy Stegbauer
@Randy: sure, for some people such a simple discipline seems to be already difficult :-) Since you mention read and write, let me just note for the records that there also are POSIX read-write locks, that could perform a bit better if there are a concurrent readers.
Jens Gustedt
A: 

The simple answers to your question are:
- No, i will be the most recent value.
- No, i does not have to be volatile.

The i_mutex protects your access to i...as long as your lock the mutex every time you read and write it. Simple as that.

In other words, you don't have to worry about caching or volatile.

Enjoy, Randy

Randy Stegbauer