99

I read somewhere that we should lock the mutex before calling pthread_cond_signal and unlock the mutex after calling it:

The pthread_cond_signal() routine is used to signal (or wake up) another thread which is waiting on the condition variable. It should be called after mutex is locked, and must unlock mutex in order for pthread_cond_wait() routine to complete.

My question is: isn't it OK to call pthread_cond_signal or pthread_cond_broadcast methods without locking the mutex?

3 Answers 3

173

If you do not lock the mutex in the codepath that changes the condition and signals, you can lose wakeups. Consider this pair of processes:

Process A:

pthread_mutex_lock(&mutex); while (condition == FALSE) pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); 

Process B (incorrect):

condition = TRUE; pthread_cond_signal(&cond); 

Then consider this possible interleaving of instructions, where condition starts out as FALSE:

Process A Process B pthread_mutex_lock(&mutex); while (condition == FALSE) condition = TRUE; pthread_cond_signal(&cond); pthread_cond_wait(&cond, &mutex); 

The condition is now TRUE, but Process A is stuck waiting on the condition variable - it missed the wakeup signal. If we alter Process B to lock the mutex:

Process B (correct):

pthread_mutex_lock(&mutex); condition = TRUE; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); 

...then the above cannot occur; the wakeup will never be missed.

(Note that you can actually move the pthread_cond_signal() itself after the pthread_mutex_unlock(), but this can result in less optimal scheduling of threads, and you've necessarily locked the mutex already in this code path due to changing the condition itself).

Sign up to request clarification or add additional context in comments.

27 Comments

@Nemo: Yes, in the "correct" path the pthread_signal_cond() can be moved after the mutex unlock, although it is probably better not to. It is perhaps more correct to say that at the point where you are calling pthread_signal_cond(), you will have already needed to have locked the mutex to modify the condition itself.
For what it's worth, it seems the question of whether to unlock before or after the signal is actually non-trivial to answer. Unlocking after ensures that a lower-priority thread won't be able to steal the event from a higher-priority one, but if you're not using priorities, unlocking before the signal will actually reduce the number of system calls/context switches and improve overall performance.
@R.. You have it backwards. Unlocking before the signal increases the number of system calls and decreases overall performance. If you signal before the unlock, the implementation knows the signal can't possibly wake a thread (because any thread blocked on the cv needs the mutex to make forward progress), allowing it to use the fast path. If you signal after the unlock, both the unlock and the signal can wake threads, meaning they're both expensive operations.
Also, even if you could defer the work of the signal to mutex unlock time, a mutex can be used simultaneously with unboundedly many condvars, so mutex unlock would become O(n) rather than O(1) which is highly undesirable from a standpoint of realtime properties.
@Alceste_: See this text in POSIX: "When a thread waits on a condition variable, having specified a particular mutex to either the pthread_cond_timedwait() or the pthread_cond_wait() operation, a dynamic binding is formed between that mutex and condition variable that remains in effect as long as at least one thread is blocked on the condition variable. During this time, the effect of an attempt by any thread to wait on that condition variable using a different mutex is undefined."
|
56

According to this manual :

The pthread_cond_broadcast() or pthread_cond_signal() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal().

The meaning of the predictable scheduling behavior statement was explained by Dave Butenhof (author of Programming with POSIX Threads) on comp.programming.threads and is available here.

2 Comments

+1 for linking the mail from Dave Butenhof. I always wondered about this issue myself, now I know... Learned something important today. Thanks.
If predictable scheduling behavior is required, then put the statements in one thread in the order that you want, or use thread priorities.
8

caf, in your sample code, Process B modifies condition without locking the mutex first. If Process B simply locked the mutex during that modification, and then still unlocked the mutex before calling pthread_cond_signal, there would be no problem --- am I right about that?

I believe intuitively that caf's position is correct: calling pthread_cond_signal without owning the mutex lock is a Bad Idea. But caf's example is not actually evidence in support of this position; it's simply evidence in support of the much weaker (practically self-evident) position that it is a Bad Idea to modify shared state protected by a mutex unless you have locked that mutex first.

Can anyone provide some sample code in which calling pthread_cond_signal followed by pthread_mutex_unlock yields correct behavior, but calling pthread_mutex_unlock followed by pthread_cond_signal yields incorrect behavior?

4 Comments

Actually, I think my question is a duplicate of this one, and the answer is "It's fine, you can totally call pthread_cond_signal without owning the mutex lock. There's no correctness issue. But in common implementations you'll miss out on a clever optimization deep inside pthreads, so it's slightly preferable to call pthread_cond_signal while still holding the lock."
I made this observation in the last paragraph of my answer.
You have a good scenario here: stackoverflow.com/a/6419626/1284631 Note that it does not claim the behaviour is incorrect, it only presents a case where the behaviour might not be as expected.
It is possible to create sample code where calling pthread_cond_signal after pthread_mutex_unlock can result in a lost wakeup because the signal is caught be the "wrong" thread, one that blocked after seeing the change in the predicate. This is only an issue if the same condition variable can be used for more than one predicate and you don't use pthread_cond_broadcast, which is a rare and fragile pattern anyway.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.