Multile pthread_cond_wait waken up and hold the mutex lock - multithreading

According to the man page, pthread_cond_broadcast wakes up all the threads that are waiting on condition variable (condvar). And those waken threads will hold back the mutex lock and return from pthread_cond_wait.
But what I am confusing is: Isn't it the mutex lock should held by only one thread in the same time?
Thanks in advance.

Condition variables work like this:
/* Lock a mutex. */
pthread_mutex_lock(&mtx);
/* Wait on condition variable. */
while (/* condition *.)
pthread_cond_wait(&cond, &mtx);
/* When pthread_cond_wait returns mtx is atomically locked. */
/* ... */
/* Unlock the mutex. */
pthread_mutex_unlock(&mtx);
So the main point to understand is that many threads can wake up when a broadcast is sent, but only one will "win" the race and actually lock mtx and get out of the loop.

Related

How a thread which is blocked while trying to mutex_lock get to know that the lock is released by another thread?

In Linux, I have a scenario where two threads execute a critical section, one acquires the lock (thread A) and the other(thread B) will wait for the lock. Later threadA releases the mutex lock. I am trying to understand how threadB will be moved to the running state and acquire the lock? How threadB(or operating system) knows that the lock is released by threadA?
I have a theory, please correct if I am wrong. threadB enters TASK_INTERRUPTABLE (blocked at the mutex and so waiting) state and it receives signal when threadA unlocks the mutex so it comes back to the running queue(TASK_RUNNING).
The Linux mutex struct keeps track of the current owner of the mutex (if any):
struct mutex {
atomic_long_t owner;
// ...
There's also a struct to keep track of what other tasks are waiting on a mutex:
/*
* This is the control structure for tasks blocked on mutex,
* which resides on the blocked task's kernel stack:
*/
struct mutex_waiter {
struct list_head list;
struct task_struct *task;
struct ww_acquire_ctx *ww_ctx;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
};
Simplifying quite a bit, when you unlock a mutex, the kernel looks at what other tasks are waiting on that mutex. It picks one of them to become the owner, sets the mutex's owner field to refer to the selected task, and removes that task from the list of tasks waiting for the mutex. At that point, there's at least a good chance that task has become un-blocked, in which case it'll be ready to run once it's unblocked. At that point, it's up to the scheduler to decide when to run it.
Optimization
Since mutexes are used a lot, and they get locked and unlocked quite a bit, they use some optimization to help speed. For example, consider the following:
/*
* #owner: contains: 'struct task_struct *' to the current lock owner,
* NULL means not owned. Since task_struct pointers are aligned at
* at least L1_CACHE_BYTES, we have low bits to store extra state.
*
* Bit0 indicates a non-empty waiter list; unlock must issue a wakeup.
* Bit1 indicates unlock needs to hand the lock to the top-waiter
* Bit2 indicates handoff has been done and we're waiting for pickup.
*/
#define MUTEX_FLAG_WAITERS 0x01
#define MUTEX_FLAG_HANDOFF 0x02
#define MUTEX_FLAG_PICKUP 0x04
#define MUTEX_FLAGS 0x07
So, when you ask the kernel to unlock a mutex, it can "glance" at one bit in the owner pointer to figure out whether this is a "simple" case (nobody's waiting on the mutex, so just mark it as unlocked, and off we go), or a more complex one (at least one task is waiting on the mutex, so a task needs to be selected to be unblocked, and be marked as the new owner of the mutex.
References
https://github.com/torvalds/linux/blob/master/include/linux/mutex.h
https://github.com/torvalds/linux/blob/master/kernel/locking/mutex.c
Disclaimer
The code extracts above are (I believe) current as I write this answer. But as noted above, mutexes get used a lot. If you look at the code for a mutex 5 or 10 years from now, chances are you'll find that somebody has done some work on optimizing the code, so it may not precisely match what I've quoted above. Most of the concepts are likely to remain similar, but changes in details (especially the optimizations) are to be expected.

Must lock be held when signaling conditional variable?

OK. The example here is provided using pthread lib in c.
In textbook I came across the following code:
//for thread 2
pthread_mutex_lock(&lock);
should_wake_up = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
This code works out pretty fine. I just wonder will the following code also works?
//for thread 2
pthread_mutex_lock(&lock);
should_wake_up = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);//signal the conditional variable but the lock is not held
What's the pros and cons for the following code?
PS. Suppose the cooperating thread has the code:
//for thread 1
pthread_mutex_lock(&lock);
while(!should_wake_up)
pthread_cond_wait(&cond, &lock);
pthead_mutex_unlock(&lock);
PS2. I came across some other question, which points out that if we don't want signal being lost, we must use lock to make sure that the associated predicate (in this case, is should_wake_up) can not be changed when the lock is held in thread 1. In this case, this seems not to be the issue. Link to the post: [1]: signal on condition variable without holding lock. I think his issue is that he forget about locking. But my question is different.
For normal usage, you can unlock the mutex before signalling a condition variable.
The mutex protects the shared state (in this case the should_wake_up flag). Provided the mutex is locked when modifying the shared state, and when checking the shared state, pthread_cond_signal can be called without the mutex locked and everything will work as expected.
Under most circumstances, I would recommend calling pthread_cond_signal after calling pthread_mutex_unlock as a slight performance improvement. If you call pthread_cond_signal before pthread_mutex_unlock then the waiting thread can be woken by the signal before the mutex is unlocked, so the waiting thread then has to go back to sleep, as it blocks on the mutex that is still held by the signalling thread.

What happens to a thread calling pthread_cond_signal?

When a thread calls pthread_cond_signal, one of the threads waiting on the condition will resume its execution. But what happens to the calling thread? Does it waits for the called thread to release the mutex and then it resumes?
For example, the waiting thread:
pthread_mutex_lock(&mut);
// ...
pthread_cond_wait(&cond, &mut);
// ...
pthread_mutex_unlock(&mut);
And the signaling thread:
pthread_mutex_lock(&mut);
// ...
pthread_cond_signal(&cond);
// ... has work to finish
pthread_mutex_unlock(&mut);
In this case, how can the signaling thread continue its work if the waiting thread has taken over the mutex?
The thread that calls pthread_cond_signal returns immediately. It does not wait for the woken thread (if there is one) to do anything.
If you call pthread_cond_signal while holding the mutex that the blocked thread is using with pthread_cond_wait, then the blocked thread will potentially wake from the condition variable wait, then immediately block waiting for the mutex to be acquired, since the signalling thread still holds the lock.
For the best performance, you should unlock the mutex prior to calling pthread_cond_signal.
Also, pthread_cond_wait may return even though no thread has signalled the condition variable. This is called a "spurious wake". You typically need to use pthread_cond_wait in a loop:
pthread_mutex_lock(&mut);
while(!ready){
pthread_cond_wait(&cond,&mut);
}
// do stuff
pthread_mutex_unlock(&mut);
The signalling thread then sets the flag before signalling:
pthread_mutex_lock(&mut);
ready=1
pthread_mutex_unlock(&mut);
pthread_cond_signal(&cond);

missed signal in c++11 condition variable

I have a small doubt about thread being woken up and unavailability of the lock
std::mutex mut;
std::queue<data_chunk> data_queue;
std::condition_variable data_cond;
void data_preparation_thread() {
while(more_data_to_prepare()) {
data_chunk const data=prepare_data();
std::lock_guard<std::mutex> lk(mut);
data_queue.push(data);
data_cond.notify_one(); //mutex is still locked here
}
}
void data_processing_thread() {
while(true) {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk,[]{return !data_queue.empty();}); //what if lk could not acquire the mutex.
data_chunk data=data_queue.front();
data_queue.pop();
lk.unlock();
process(data);
if(is_last_chunk(data))
break;
}
}
In the above example data_preparation_thread() puts data in the queue and notifies and thread waiting on the condition_variable.
My question, if the other thread wakes up and finds the associated mutex is still not available, it sleeps again. Isn't it a condition of missed signal ?
if the other thread wakes up and finds the associated mutex is still not available, it sleeps again
Once it re-acquires the mutex it proceeds to test the condition.
Condition variable notification is essentially a hint that the condition may have changed and needs to be re-evaluated. There can be spurious wake-ups. The code waits for the condition to become true, not for the signal.
There's a difference between "sleeping" on the condition variable (i.e. waiting for a signal) and "sleeping" on the mutex (i.e. waiting to lock it).
If the thread wakes up from waiting on the condition variable and the mutex is still locked it starts waiting on the mutex, until it can acquire it and then check the condition (i.e. the predicate). That's not the same as waiting on the condvar again, so nothing has been missed. It's still waiting to check if the condition is true, which it can't do until it acquires the mutex lock.
Assuming that you correctly check the condition when waking (which is what the predicate you pass to condition_variable::wait does) then you won't miss the event that caused the signal.

Is a thread holding the respective mutex on a spurious wakeup?

Let's take a look at the code below.
Suppose a thread sees ready=false and therefore waits on the condition variable *mv_cv*, hence releasing the mutex *my_mutex* and putting itself to sleep.
Some time later, something spuriously wakes the thread up while ready still holds the value false. My question is:
Is the thread now holding the mutex *my_mutex* by reacquiring the mutex before waking up?
pthread_mutex_lock(&my_mutex);
while ( !ready )
{
pthread_cond_wait(&my_cv, &my_mutex);
}
//some operation goes here
pthread_mutex_unlock(&my_mutex);
Yes. Spurious wake-up is one kind of successful return, and post condition (reacquiring lock of the mutex) will be fulfilled.

Resources