0

I've written a little to program to try out pthread conditional waits. But the problem is that there is no guarantee that a signal when sent out will be caught, thereby the thread losing the wakeup. How do I get around this?

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

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *thread_func1(void* arg){
  printf("thread1 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread1: signalling\n");
  pthread_cond_signal(&cond);
  printf("thread1: signalled\n");
  pthread_mutex_unlock(&mutex);
  printf("thread1: exiting\n");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..\n");
  pthread_cond_wait(&cond, &mutex);
  printf("thread2: signal received\n");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting\n");
  pthread_exit(0);
}

int main(int argc, char** argv){
  pthread_t thread1, thread2;

  pthread_create(&thread1, NULL, thread_func1, NULL);
  pthread_create(&thread2, NULL, thread_func2, NULL);

  pthread_join(thread1, 0);
  pthread_join(thread2, 0);

  return 0;
}

Here is an output from a run:

thread1 started
thread1: signalling
thread2 started
thread2: waiting for signal..
thread1: signalled
thread1: exiting
// nothing happens now; where is the signal??

Here's from another one (which works):

thread2 started
thread2: waiting for signal..
thread1 started
thread1: signalling
thread1: signalled
thread1: exiting
thread2: signal received
thread2: exiting
// program successfully exits

I'm not concerned about any kind of critical section for now, so I haven't used any locks.

How do I ensure this thing works for each run?

Edit: I have edited the code as per alk's answer below. I have added the initializers and locks. The original code I posted is here.

Siddhant
  • 53
  • 1
  • 12

2 Answers2

2

As you have noticed, thread 1 might signal the condition variable before thread 2 calls pthread_cond_wait(). The condition variable does not "remember" that it has been signaled, so the wakeup will be lost. Therefore, you need to use some kind of variable to determine whether thread 2 needs to wait.

int signalled = 0;

void *thread_func1(void* arg){
  printf("thread1 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread1: signalling\n");
  signalled = 1;
  pthread_cond_signal(&cond);
  printf("thread1: signalled\n");
  pthread_mutex_unlock(&mutex);
  printf("thread1: exiting\n");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..\n");
  if(!signalled) {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("thread2: signal received\n");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting\n");
  pthread_exit(0);
}

However, this code is still not correct. The pthreads spec states that "spurious wakeups" may occur on condition variables. This means that pthread_cond_wait() might return even if nobody has called pthread_cond_signal() or pthread_cond_broadcast(). Therefore, you need to check the flag in a loop, rather than just once:

void *thread_func2(void* arg){
  printf("thread2 started\n");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..\n");
  while(!signalled) {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("thread2: signal received\n");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting\n");
  pthread_exit(0);
}

Update: An alternate method to combine the function of a condition variable and a counter is to use a semaphore.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t sem;

void *thread_func1(void* arg){
  printf("thread1 started\n");
  printf("thread1: signalling\n");
  sem_post(&sem);
  printf("thread1: signalled\n");
  printf("thread1: exiting\n");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started\n");
  printf("thread2: waiting for signal..\n");
  sem_wait(&sem);
  printf("thread2: signal received\n");
  printf("thread2: exiting\n");
  pthread_exit(0);
}

int main(int argc, char** argv){
  pthread_t thread1, thread2;

  sem_init(&sem);

  pthread_create(&thread1, NULL, thread_func1, NULL);
  pthread_create(&thread2, NULL, thread_func2, NULL);

  pthread_join(thread1, 0);
  pthread_join(thread2, 0);

  sem_destroy(&sem);

  return 0;
}
augurar
  • 12,081
  • 6
  • 50
  • 65
  • This is a workaround. In that case, I don't even need any kind of condition variable or mutex and the signalled variable is sufficient. The point is to get the wait and signal working as expected. – Siddhant Mar 16 '14 at 03:57
  • @me.sid No, you misunderstand. Without the condition variable, this would simply be a "busy loop", where the waiting thread would be wasting processor time. Additionally, this generalizes to more complicated situations (e.g. multiple waiting threads, more complicated wait condition, etc.). – augurar Mar 16 '14 at 17:20
  • @me.sid Another way to do this is with semaphores, I'll add that on to my answer. – augurar Mar 16 '14 at 17:21
  • @me.sid This is not a workaround, this is a correct use of a pthread_cond_t. You cannot use a pthread_cond_t without a predicate, and if you want to relinquish the CPU, you do need a condition variable (or a semaphore or the like) – nos Mar 16 '14 at 18:07
  • I can't see the use of the condition variable if I can wait on the signalled variable to become 1. If signalled becomes 1, we can simply come out of the busy waiting loop. How does the pthread_cond_wait help here? – Siddhant Mar 17 '14 at 12:22
  • @me.sid The condition variable allows you to sleep (and release the mutex) while waiting for the flag to be set. But because of the possibility of spurious wakeups, you must check the flag in a loop. Usually you will only have to call `pthread_cond_wait()` at most once. – augurar Mar 17 '14 at 21:38
  • I think now I get you. Having a busy waiting loop on a flag without any condition variable will eat up processing power whereas a conditional wait actually _de-schedules_ the thread till a signal is received. But if the signal is sent before anyone calls pthread_condition_wait() and if we want the signal to persist, we use the predicate flag to say that the signal was sent sometime in the past. Right? – Siddhant Mar 21 '14 at 07:05
  • @me.sid Yes. Furthermore, calling `pthread_cond_wait()` releases the mutex, allowing the signalling thread to acquire it. The mutex is re-acquired by the waiting thread when it wakes up. – augurar Mar 21 '14 at 20:57
0

For starters, your code misses to initialise both, mutex and cond.

To do so, at least do:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

Also the mutex passed to pthread_cond_wait() shall be locked. And it is locked when then function returns.


Update:

Your code introduces a race. That is: if the thread 1 signals before thread 2 waits for the signal, thread 2 will wait forever, which most likely happend in the first trace you show.

alk
  • 69,737
  • 10
  • 105
  • 255
  • Yeah. Now, after initialising and also adding lock/unlock around the signal and wait statements, there still seems to be no success. – Siddhant Mar 15 '14 at 18:09
  • Have a look at the edited code above. Shouldn't the mutex locks help avoid races? – Siddhant Mar 15 '14 at 18:23
  • @me.sid: There is no reason why thread 1 will necessarily lock the mutex first in any case. Just starting thread 1 before thread 2 is not enough. – alk Mar 16 '14 at 07:57