Does a pthread_cond_signal or pthread_cond_broadcast call imply a write memory barrier?
Asked Answered
I

4

5

Condition variables are generally used such that the state they refer to is modified under a mutex. However, when the state is just a single set-only flag, there's no need for a mutex to prevent simultaneous execution. So one might want to do something like this:

flag = 1;
pthread_cond_broadcast(&cvar);

However, this is only safe if pthread_cond_broadcast implies a write memory barrier; otherwise, the waiting thread may see the condition variable broadcast before the flag write. That is, the waiting thread may awaken, consume the cvar signal, but see the flag still 0.

So, my question is: Do the pthread_cond_broadcast and pthread_cond_signal calls imply a write memory barrier? If so, where is this specified in the relevant POSIX (or other) specifications? The spec seemed unclear on this point.

Note: I am aware that, in practice, this does result in a memory barrier (on Linux, because thread awakening implies a full CPU memory barrier, and the cross-library function call implies a compiler memory barrier). However, I'm interested here in what the spec guarentees.

Interglacial answered 29/8, 2011 at 19:29 Comment(0)
H
9

Regardless of whether it implies a memory barrier, the code is still not correct. Consider the read side:

while (flag == 0)
    pthread_cond_wait(&cvar, &mutex);

If the read side is suspended between testing flag == 0 and executing the wait, the write side can execute the flag = 1; pthread_cond_signal(&cvar);. The read side will then miss the wakeup entirely - it will wait forever. Remember that wakeups are not queued - if there's no waiter when the condition variable is signalled, the signal has no effect. To avoid this, the write side needs to lock the mutex anyway.

Hallow answered 30/8, 2011 at 12:22 Comment(1)
Technically POSIX does promise that pthread_cond_broadcast() is a memory barrier (pubs.opengroup.org/onlinepubs/9699919799/basedefs/…). But as your answer details nicely, that's not useful for the purpose asked about.Hydrosphere
D
3

Under POSIX, if you write to a variable from one thread and read it from another then you must protect it with a mutex. There is no exception made for pthread_cond_broadcast.

If your platform/compiler offer atomic variables then they may make additional guarantees about those. For example, if flag is a C++11 std::atomic<int> then this code is OK.

Demilune answered 30/8, 2011 at 11:19 Comment(0)
S
0

The compiler is entitled to assume that non volatile objects do not have their value spuriously changed. It's essentially to be able to assume that for even the most simple CSE optimization to be valid (and it makes the optimization undetectable).

It is a fundamental invariant and the basis of any local reasoning on mutable state.

Such modification of even types that have atomic loads and stores at the CPU level may work for non/limited optimized compilations and fail at higher optimization when the compiler is allowed to analyse the program to infer stuff.

So: don't do it.

Slurry answered 15/8, 2020 at 22:25 Comment(0)
K
0

As my knowledge, the pthread_cond_signal() and pthread_cond_broadcast don't need a memory barrier, because pthread_cond_wait() is called with mutex locked, the writer need also lock the mutex while it change the variable, when writer release the lock, there's a memory barrier.

pthread_mutex_lock(&lock);
flag = 1;
pthread_mutex_unlock(&lock);    // memory barrier here
pthread_cond_broadcast(&cvar);

call pthread_cond_broadcast() before pthread_mutex_unlock() is also right

Katusha answered 30/4 at 2:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.