pthread_cond_wait versus semaphore
Asked Answered
M

5

49

What are the pros / cons of using pthread_cond_wait or using a semaphore ? I am waiting for a state change like this :

pthread_mutex_lock(&cam->video_lock);
while(cam->status == WAIT_DISPLAY) {
    pthread_cond_wait(&cam->video_cond, &cam->video_lock);
}
pthread_mutex_unlock(&cam->video_lock);

Using a properly initialised semaphore, I think I could do it like this :

while(cam->status == WAIT_DISPLAY) {
    sem_wait(&some_semaphore);
}

What are the pros and cons of each method ?

Malocclusion answered 16/9, 2008 at 9:40 Comment(2)
I just realized that "properly initialized semaphore" is ill-defined. Is the semaphore set to 1 or to 0? I'd say it should be set to 0. Then, does the semaphore protect cam->status or not?Vernita
In the second snippet, as mentioned in other answers, your thread will be blocked while mutex isn't released. So your sem_wait method will never return because no other thread can acquire the mutex and call sem_signal. However, I won't what if I release mutex before sem_wait and require it again after waiting. I know these steps are not atomic, so what will happen?Veronikaveronike
C
66

A semaphore is suited cleanly to a producer-consumer model, although it has other uses. Your program logic is responsible for ensuring that the right number of posts are made for the number of waits. If you post a semaphore and nobody is waiting on it yet, then when they do wait they continue immediately. If your problem is such that it can be explained in terms of the count value of a semaphore, then it should be easy to solve with a semaphore.

A condition variable is a bit more forgiving in some respects. You can for example use cond_broadcast to wake up all waiters, without the producer knowing how many there are. And if you cond_signal a condvar with nobody waiting on it then nothing happens. This is good if you don't know whether there's going to be a listener interested. It is also why the listener should always check the state with the mutex held before waiting - if they don't then they can miss a signal and not wake up until the next one (which could be never).

So a condition variable is suitable for notifying interested parties that state has changed: you acquire the mutex, change the state, signal (or broadcast) the condvar and release the mutex. If this describes your problem you're in condvar territory. If different listeners are interested in different states you can just broadcast and they'll each in turn wake up, figure out whether they've found the state they want, and if not wait again.

It's very gnarly indeed to attempt this sort of thing with a mutex and a semaphore. The problem comes when you want to take the mutex, check some state, then wait on the semaphore for changes. Unless you can atomically release the mutex and wait on the semaphore (which in pthreads you can't), you end up waiting on the semaphore while holding the mutex. This blocks the mutex, meaning that others can't take it to make the change you care about. So you will be tempted to add another mutex in a way which depends on your specific requirements. And maybe another semaphore. The result is generally incorrect code with harmful race conditions.

Condition variables escape this problem, because calling cond_wait automatically releases the mutex, freeing it for use by others. The mutex is regained before cond_wait returns.

IIRC it is possible to implement a kind of condvar using only semaphores, but if the mutex you're implementing to go with the condvar is required to have trylock, then it's a serious head-scratcher, and timed waits are out. Not recommended. So don't assume that anything you can do with a condvar can be done with semaphores. Plus of course mutexes can have nice behaviours that semaphores lack, principally priority-inversion avoidance.

Cuzco answered 20/9, 2008 at 18:21 Comment(0)
N
20

Conditionals let you do some things that semaphores won't.

For example, suppose you have some code which requires a mutex, called m. It however needs to wait until some other thread has finish their task, so it waits on a semaphore called s. Now any thread which needs m is blocked from running, even though the thread which has m is waiting on s. These kind of situations can be resolved using conditionals. When you wait on a conditional, the mutex currently held is released, so other threads can acquire the mutex. So back to our example, and suppose conditional c was used instead of s. Our thread now acquires m, and then conditional waits on c. This releases m so other threads can proceed. When c becomes available, m is reacquired, and our original thread can continue merrily along its way.

Conditional variables also allows you to let all threads waiting on a conditional variable to proceed via pthread_cond_broadcast. Additionally it also allows you to perform a timed wait so you don't end up waiting forever.

Of course, sometimes you don't need conditional variables, so depending on your requirements, one or the other may be better.

Nolita answered 16/9, 2008 at 11:15 Comment(1)
This is a good answer. To add to the description, what makes conditionals unique relative to the described combination of the mutex and the semaphore is that the conditional manipulates the mutex/semaphore pair atomically!!Fleeting
V
5

The 2nd snippet is racy, don't do that.

The other answers have a nice discussion of the relative merits; I'll just add that pthread_cond_broadcast is a clear advantage of condition variables.

Beyond that, I'm just more used to condition variables for that, as they are what you use in Java, even because they help you to avoid races when checking the shared flags.

Indeed, in the 2nd snippet you don't have any lock protecting the read of cam->status, so it is accessed through a data race. Most platforms will let you get away with that in this particular example, but that has undefined semantics, by POSIX and by the memory model of the next C/C++ standards.

In fact, a real race condition is possible if another thread allocates a new cam structure and overwrites cam; the waiting thread might see the update to the 'cam' pointer without seeing the initialization of cam->status. Indeed, the 2nd snippet is asking for trouble, in this case and in general.

http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/

Vernita answered 12/1, 2009 at 9:11 Comment(1)
In fact, in the second snippet the semaphore itself might be protecting (or not) the status flag (though I cannot imagine a good locking protocol for that).Vernita
F
0

In your second snippet, you're getting the lock multitude of times, never releasing it.

In general, the state you're waintin on can be completely expressed by a semaphore, then you can use just that. A lock structure is smaller in size, and it requires less atomic operations to check/set/release.

Otherwise, if the state is complex, and different parts of the code wait on different conditions of the same variable (eg, here you want x<10; there you want y>x), use cond_wait.

Faubourg answered 16/9, 2008 at 10:10 Comment(3)
The 2nd snippet is correct - a wakeup is always caused by somebody signaling the semaphore.Vernita
@Blaisorblade, the wake up is correct, the status check is no because it is not guarded.Wearisome
That's a different problem than what this answer describes — "in your second snippet, you're getting the lock multitude of times, never releasing it". I mention this problem in my answer above (though not very specifically). But while looking again at the code, I realized one cannot really tell whether the second snippet is correct or not because too many details are missing (see my comment to the question). But I phrased my comment incorrectly — I should have said "the problem you mention is not there, because ...".Vernita
D
0
while(cam->status == WAIT_DISPLAY) {
    sem_wait(&some_semaphore);
}

This is completely wrong. This is prone to race condition. By the time the thread blocks on sem_Wait, the condition cam->status == WAIT_DISPLAY may not hold good in the system as some other thread may have modified it. So, your thread is now sleeping for all wrong reasons.

Dingbat answered 28/2, 2022 at 6:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.