2

If I have a piece of code like this

pthread_cond_t c;
pthread_mutex_t m;
int var = 0;

void some_function(int *some_variable)
{
    pthread_mutex_lock(&m);
    while(*some_variable != 123)
        pthread_cond_wait(&c, &m);
    pthread_mutex_unlock(&m);
    // *some_variable++; (1)
}

void some_another_fun(int *some_variable)
{
    pthread_mutex_lock(&m);
    *some_variable = 123;
    pthread_cond_signal(&c);
    pthread_mutex_unlock(&m);
}

int main()
{
    // run 1 thread for some_function
    // and one for some_another_fun
    // pass `&var` to both of them
}

Should I declare some_variable or var as volatile in this case? Should I declare it as volatile if (1) is uncommented (i.e. *some_variable changes in some_function)?

Can a compiler cache *some_variable value in a register before executing while and never update it again?

I don't fully understand when I should use volatile keyword (even this answers has some contradiction and disagreements) thus this question.

PepeHands
  • 1,368
  • 5
  • 20
  • 36
  • Your code example has a deadlock. `some_function()` locks `m` and waits for `some_other_functon()` to modify the shared variable, but to do so it must first aquire the `m` that `some_function()` has locked. It will only run if `some_other_functon()` runs and locks the semaphore first. – Clifford Feb 24 '18 at 15:42
  • @Clifford I actually did not try to write 100% correct code, as I only intended to demonstrate the general pattern. Nevertheless, I don't think I understand your comment. Mutex `m` will be unlocked by `pthread_cond_wait` so `some_another_fun` will be able to acquire it and store 123 to `some_variable` – PepeHands Feb 24 '18 at 17:24
  • I am no pthreads expert, so you are probably correct. My experience is with RTOS on embedded systems and to be frank POSIX threading semantics is somewhat alien to me. – Clifford Feb 24 '18 at 18:28

1 Answers1

3

The volatile is not needed because pthread functions contain a memory fence. See an answer to a similar question: Does pthread_mutex_lock contains memory fence instruction?

An important thing to note is that volatile does not mean that the access must be performed in any particular order compared to non-volatile accesses. This is why a memory fence is needed in inter-thread communication, instead of just having some global flags that we mark volatile(unless we mark everything in a program as volatile).

afic
  • 500
  • 4
  • 13
  • Yes, I'm aware that they have memory fences, but the problem here is that _compiler_ can optimize that while loop. Memory fences only help with CPU reordering. – PepeHands Feb 24 '18 at 20:36
  • The compiler must respect memory fences in order to be correct. Due to the memory fence inside of the while loop the compiler can not optimize out the while loop's check. – afic Feb 24 '18 at 20:42
  • But if it just caches the value of `*some_variable` in some register before the first iteration of while loop and then does all the checks with this value? Do memory fences prevent it as well? – PepeHands Feb 25 '18 at 08:15
  • Yeah, seems that you were right. I've read about "memory" argument in `asm volatile ("" ::: "memory")` and it indeed does what asked about, thanks! – PepeHands Feb 25 '18 at 08:40