understanding of pthread_cond_wait() and pthread_cond_signal()
Asked Answered
M

3

77

Generally speaking, pthread_cond_wait() and pthread_cond_signal() are called as below:

//thread 1:
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
do_something()
pthread_mutex_unlock(&mutex);

//thread 2:
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);  
pthread_mutex_unlock(&mutex);

The steps are

  1. pthread_cond_wait(&cond, &mutex); is called, it unlocks the mutex

  2. Thread 2 locks the mutex and calls pthread_cond_signal(), which unlocks the mutex

  3. In thread 1, pthread_cond_wait() is called and locks the mutex again

Now in thread 2, after pthread_cond_signal() is called, pthread_mutex_unlock(&mutex) is going to run, it seems to me that it wants to unlock a the mutex which is now locked by thread 1. Is there anything wrong in my understanding?

Besides, it also seems to me that pthread_cond_wait() can be called by only 1 thread for the same cond-mutex pair. But there is a saying "The pthread_cond_signal() function shall unblock at least one of the threads that are blocked on the specified condition variable cond (if any threads are blocked on cond)." So, it means pthread_cond_wait() can be called by many threads for the same cond-mutex pair?

Marrowbone answered 13/5, 2013 at 13:7 Comment(0)
I
146

pthread_cond_signal does not unlock the mutex (it can't as it has no reference to the mutex, so how could it know what to unlock?) In fact, the signal need not have any connection to the mutex; the signalling thread does not need to hold the mutex, though for most algorithms based on condition variables it will.

pthread_cond_wait unlocks the mutex just before it sleeps (as you note), but then it reaquires the mutex (which may require waiting) when it is signalled, before it wakes up. So if the signalling thread holds the mutex (the usual case), the waiting thread will not proceed until the signalling thread also unlocks the mutex.

The common use of condition vars is something like:

thread1:
    pthread_mutex_lock(&mutex);
    while (!condition)
        pthread_cond_wait(&cond, &mutex);
    // do something that requires holding the mutex and condition is true
    pthread_mutex_unlock(&mutex);

thread2:
    pthread_mutex_lock(&mutex);
    // do something that might make condition true
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);

The two threads have some shared data structure that the mutex is protecting access to. The first thread wants to wait until some condition is true then immediately do some operation (with no race condition opportunity for some other thread to come in between the condition check and action and make the condition false.) The second thread is doing something that might make the condition true, so it needs to wake up anyone that might be waiting for it.

Ignoramus answered 13/5, 2013 at 14:10 Comment(12)
you see steps: (1) thread 2 locks mutext (2) in thread 2, pthread_cond_signal is called (3) in thread 1,pthread_cond_wait is signaled, it needs to reacquire the mutext, but now it is locked by steps (1), is it right?Marrowbone
@user1944267: thread 1 won't be able to reaquire the lock (and continue) until thread 2 calls pthread_mutex_unlock. But since that happens immediately after the call the pthread_cond_signal returns, there will be very little delay.Ignoramus
@ChrisDodd, Just one question here in thread 1, just before while loop, i think that we should "do something that trigger thread2" because if thread is triggerer before thread 1 execute pthread_wait_cond the signal is lost and thread 1 will be in an infinite waitLaunalaunce
@Mouin: In that case the condition will be true and thread 1 will not wait. The idea is that 'condition' and the action after it is something that cannot be checked and done atomically (some complex condition and/or action), so using the mutex and condition var makes it effectively atomic -- as long as noone does anything that affects the condition without holding the lock.Ignoramus
@ChrisDodd, thx for the feedback "In that case the condition will be true and thread 1 will not wait": by triggering thread 2 i don't mean set condition true. In my opinion thread 1 has to tell thread 2 just after locking the mutex : "go ahead do your check and set condition to true", at that point thread 2 will try to set condition but get blocked on the mutex only when thread 1 execute pthread_cond_wait that it get the mutex. but in the above code we cannot guarantee that thread 2 will call pthread_cond_signal after thread1 execute it's wait.Launalaunce
Thread 2 does not check the condition -- it just just does something unconditionally that might affect the condition. As long as that something is done while it holds the lock, then it can't happen between the test and cond_wait in thread 1. If thread2 is guarenteed to loop repeatedly doing something atomic and calling cond_signal, it need not even hold the lock -- thread 1 might miss the first signal, but will get the next one.Ignoramus
@ChrisDodd In your answer you said, "but then it re-acquires the mutex (which may require wait)" can this senerio be possible: (1) A thread t1 waits and release the mutex. (2) A different thread t2 signals changing the condition and then eventually release the mutex. Is it possible that as t1 tries to re-acquire the mutex back from t2, it becomes context switched by the OS and hypothetically a new thread t3 grabs the mutex mutating shared variables, and as t1 jumps back only to see different values? Can this race condition occur?Crossstitch
@Miket25: depends on if your mutexes are fair or not. With strictly fair mutexes, they'll always be aquired by whichever thread has been waiting the longest. With unfair mutexes, another thread might jump the queue as you note, causing some thread to starve (though progress will always be made in that the jumping thread will get the mutex and make progress).Ignoramus
Mouin has a point. The pthread_cond_signal() call does not queue up the signal therefore if this call happens ahead before any call to pthread_cond_wait(), the signal is lost.Inchmeal
@ifelsemonkey: If there hasn't been a call to cond_wait, then the cond_signal call is unnecessary, but harmless. The point of cond_signal is to signal a waiting process to reevaluate the condition IF ONE EXISTS. If there is no waiting process, then noone is waiting and noone needs to be notified -- any process that wants to acquire the resource (later) will evaluate the condition before calling cond_wait.Ignoramus
@ChrisDodd, I disagree with regards to what you considered as "unnecessary". A signal, has a meaning and must not be lost, irregardless of the order of T1 and T2. The signal must be captured, in all cases. The boolean condition is what captures the signal. In conclusion, these three plays an important role: mutex, condition variable, and the boolean. If one of these is ignored, then the situation cannot be reasoned with.Inchmeal
@truthadjustr: condtion is a placeholder for a process that tests something and computes a boolean. In itself it does not capture anything, but it is accessing other data structures protected by the mutex. It is those data structures that capture things, and the signal from the condition var just tells someone that the condition must be recomputed. If noone has a cached condition value that needs to be thrown away, then the signal is meaningless and can be ignored -- anyone who wants to know the condition will be recomputing it from scratch anyways.Ignoramus
A
14

Here is a typical example: thread 1 is waiting for a condition, which may be fulfilled by thread 2.

We use one mutex and one condition.

pthread_mutex_t mutex;
pthread_cond_t condition;

thread 1 :

pthread_mutex_lock(&mutex); //mutex lock
while(!condition){
    pthread_cond_wait(&condition, &mutex); //wait for the condition
}

/* do what you want */

pthread_mutex_unlock(&mutex);

thread 2:

pthread_mutex_lock(&mutex);

/* do something that may fulfill the condition */

pthread_mutex_unlock(&mutex);
pthread_cond_signal(&condition); //wake up thread 1

Edit

As you can see in the pthread_cond_wait manual:

It atomically releases mutex and causes the calling thread to block on the condition variable cond; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable".

Alaniz answered 13/5, 2013 at 14:42 Comment(5)
you see steps: (1) thread 2 locks mutext (2) in thread 2, pthread_cond_signal is called (3) in thread 1,pthread_cond_wait is signaled, it needs to reacquire the mutext, but now it is locked by steps (1), is it right?Marrowbone
The trick is, in thread 1, pthread_cond_wait temporarily releases the mutex.Alaniz
1) TH1 locks the mutex 2) TH1 unlocks the mutex (with pthread_cond) 3) TH2 locks the mutex 4) TH2 unlocks the mutex and sends the signal 5) TH1 gets the mutex back 6) TH1 unlocks the mutexAlaniz
in thread2, pthread_cond_signal can also be signalled while the mutex is locked.Bently
when there are more than one "thread2" instances and the "others" may be waiting for the mutex lock, do I have to check condition to be false, when i "do something that may fulfill", because it's not safe the next thread that will get the mutex is thread1 ?Stagey
G
0

I have taken example from here https://www.geeksforgeeks.org/condition-wait-signal-multi-threading/

and modified to this,

#include <pthread.h> 
#include <stdio.h> 
#include <unistd.h> 

// Declaration of thread condition variable 
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 

// declaring mutex 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 

// Thread function 
void releaseFun() 
{ 
    // Let's signal condition variable cond
    printf("Signaling condition variable cond\n"); 
    pthread_cond_signal(&cond); 
}

// Thread function 
void* blockedThread() 
{
    // acquire a lock 
    pthread_mutex_lock(&lock); 
    printf("Waiting on condition variable cond\n");
    pthread_cond_wait(&cond, &lock); 
    // release lock 
    pthread_mutex_unlock(&lock); 

    printf("Returning thread\n"); 

    return NULL; 
}    

// Driver code 
int main() 
{ 
    pthread_t tid;

    // Create thread 1 
    pthread_create(&tid, NULL, blockedThread, NULL); 

    // sleep for 1 sec so that thread 1 
    // would get a chance to run first 
    sleep(1); 

    releaseFun();
    // wait for the completion of thread 2 
    pthread_join(tid, NULL); 

    return 0; 
}

Output: gcc test_thread.c -lpthread

Waiting on condition variable cond

Signaling condition variable cond

Returning thread

Locking and unlocking of the thread will happen only when blockThread's pthread_cond_wait() function is signaled to be unblocked.

Goldia answered 16/9, 2020 at 8:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.