3
votes

So I have searched high and low on stack overflow and other resources but I am unable to understand a few things with regards to the aforementioned functions. Specifically,

1)When pthread_cond_timedwait() returns because a timer value has run out, how does it automatically re-acquire the mutex. The mutex could potentially be locked elsewhere. For instance, in say a producer-consumer queue, the consumer could be waiting using the timedwait() variant. From explanations I have read, it appears that the mutex will automatically be re-acquired when the thread wakes up. This baffles me? Or is it that it will wake up after the timer runs out and then check the predicate and lock again. That makes no sense?

2)When a bunch of threads have acquired a mutex and are now all waiting on a simple pthread_cond_wait(), what will happen if another thread issues a pthread_cond_broadcast(). Will each of the threads (as determined by the scheduler) wake up one by one, acquire the mutex in sequence and then each of them proceed? Or will only one thread of the entire list of threads wake up. I would assume that the latter of the two behaviors would be possible using pthread_cond_signal() as well. Also, if the previous approach is the right behavior, then we can assume that some other thread might end up calling wait() on the variable somewhere else. Would that then put all these other waiting threads back into a blocked state and waiting for a signal. Or would each of them continue to wake up and make progress one by one?

2

2 Answers

2
votes

(1) pthread_cond_timedwait()

A short (and shallow) explanation how it works.

1) The mutex, associated with the pthread_cond_timewait() should be locked before a call to the function. It is your responsibility to do it. Otherwise, the function behavior is undefined.

2) When your program transfers its control to the function, the function ATOMICALLY releases the associated mutex, giving other threads a chance to acquire it. When you are waiting, the mutex is unlocked. It is the function responsibility to unlock it. If the function implementation could not do it, only one single thread would deal with the associated mutex.

3) When the function returns, either as a result of timeout or as a result of receiving a signal, the function ATOMICALLY locks the mutex back. It is the function responsibility to lock the mutex.

4) Now the mutex should be unlocked again by you.

The word "atomically" in the explanation means, that the function itself is thread safe.

(2) pthread_cond_broadcast()

Signaling with pthread_cond_broadcast() causes to all waiting threads to wake up and start processing one by one. Signaling with pthread_cond_signal() causes only one single thread to wake up. To "feel" the concept you may play with the following code that illustrates the idea. Replace the pthread_cons_signal with the pthread_cond_brodcast. Take into consideration that the program will never terminate when pthread_cons_signal is used: only one waiter thread gets the signal and escapes from the wait loop.

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int value = 0;

void* waiter(void* arg)
{
  int* tid = (int*)arg;

  printf("waiter started %d\n", *tid);

  pthread_mutex_lock(&mutex);
  while(value == 0)
  {
    pthread_cond_wait(&cond, &mutex);
  }
  sleep(10);
  printf("waiter %d releases\n", *tid);
  pthread_mutex_unlock(&mutex);

}

void* notifier(void* arg)
{
  sleep(2);
  pthread_mutex_lock(&mutex);
  value = 1;
  //pthread_cond_broadcast(&cond);
  pthread_cond_signal(&cond);
  pthread_mutex_unlock(&mutex);
}

int main(void)
{
  pthread_t w1; // waiter
  int tid1=1;
  pthread_t w2; // waiter
  int tid2=2;
  pthread_t n1; // notifier

  pthread_create(&w1, NULL, waiter, &tid1);
  pthread_create(&w2, NULL, waiter, &tid2);
  pthread_create(&n1, NULL, notifier, NULL);

  pthread_join(w1, NULL);
  pthread_join(w2, NULL);
  pthread_join(n1, NULL);
  return 0;
}  
1
votes

If pthread_cond_timedwait() returns due to the timeout expiring, it will reacquire the mutex before returning. This might mean that it has to wait for the mutex to be unlocked, if it is locked by another thread at that time. Essentially, it behaves as if it calls pthread_mutex_lock() before returning.

When pthread_cond_broadcast() is called, all of the waiting threads will be woken, which means that they will all try to acquire the mutex and return. The mutex will serialise this, of course, so they will only return one-at-a-time. It doesn't matter what happens after this point - those threads will all eventually return from their pthread_cond_wait() call as soon as they can acquire the mutex. Another thread calling pthread_cond_wait() does not affect this.