21

I am trying to learn basics of pthread_cond_wait. In all the usages, I see either

if(cond is false)
   pthread_cond_wait

or

while(cond is false)
   pthread_cond_wait

My question is, we want to cond_wait only because condition is false. Then why should i take the pain of explicitly putting an if/while loop. I can understand that without any if/while check before cond_wait we will directly hit that and it wont return at all. Is the condition check solely for solving this purpose or does it have anyother significance. If it for solving an unnecessary condition wait, then putting a condition check and avoiding the cond_wait is similar to polling?? I am using cond_wait like this.

void* proc_add(void *name){
    struct vars *my_data = (struct vars*)name;
    printf("In thread Addition and my id = %d\n",pthread_self());
    while(1){
    pthread_mutex_lock(&mutexattr);
    while(!my_data->ipt){  // If no input get in
            pthread_cond_wait(&mutexaddr_add,&mutexattr);  // Wait till signalled
            my_data->opt = my_data->a + my_data->b;
            my_data->ipt=1;
            pthread_cond_signal(&mutexaddr_opt);
    }
    pthread_mutex_unlock(&mutexattr);
    if(my_data->end)
            pthread_exit((void *)0);
    }
}

The logic is, I am asking the input thread to process the data whenever an input is available and signal the output thread to print it.

sevko
  • 1,402
  • 1
  • 18
  • 37
CHID
  • 1,625
  • 4
  • 24
  • 43

2 Answers2

29

You need a while loop because the thread that called pthread_cond_wait might wake up even when the condition you are waiting for isn't reached. This phenomenon is called "spurious wakeup".

This is not a bug, it is the way the conditional variables are implemented.

This can also be found in man pages:

Spurious wakeups from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur. Since the return from pthread_cond_timedwait() or pthread_cond_wait() does not imply anything about the value of this predicate, the predicate should be re-evaluated upon such return.

Update regarding the actual code:

void* proc_add(void *name) 
{
    struct vars *my_data = (struct vars*)name;

    printf("In thread Addition and my id = %d\n",pthread_self());

    while(1) {

        pthread_mutex_lock(&mutexattr);

        while(!my_data->ipt){  // If no input get in
            pthread_cond_wait(&mutexaddr_add,&mutexattr);  // Wait till signalled
        }

        my_data->opt = my_data->a + my_data->b;
        my_data->ipt=1;
        pthread_cond_signal(&mutexaddr_opt);

        pthread_mutex_unlock(&mutexattr);

        if(my_data->end)
            pthread_exit((void *)0);
        }
    }
}
Burrough Clarke
  • 522
  • 4
  • 19
Maksim Skurydzin
  • 10,301
  • 8
  • 40
  • 53
  • Hi Maxim, Thanks for your reply. I went through spurious wakeups. Get it now. but, as soon as i hit the cond_wait, the call is going to block and the condition will be checked in next iteration? So still it can wake up from spurious calls? – CHID Oct 29 '12 at 13:22
  • Your code is waiting for the input to be available. I think, you should wait for this condition to be true in a loop: `while ( !input_available ) { pthread_cond_wait(&mutexaddr_add,&mutexattr); }` – Maksim Skurydzin Oct 29 '12 at 13:25
  • Yes i tried that. But again after processing the input and signalling the output thread, i have to wait for next input. So I used the while loop. In my case, the thread has to always wait for input. So i got a doubt that the purpose would be solved by cond_wait and we dont need an if/while check before the actual cond_wait – CHID Oct 29 '12 at 13:29
  • 1
    I have updated the answer regarding your code example. That's how I would do that. Btw, there are many different schemes for locking/unlocking mutexes used with condition variables. Here is a good answer about this - http://stackoverflow.com/a/2763749/276274 – Maksim Skurydzin Oct 29 '12 at 13:39
  • Thanks for the update Maxim. For one last time. the variable my_data->ipt should always be 0. I dont want the statement `my_data->ipt=1` as it might cause an invalid signal to output thread while coming for next time. Ultimately, everytime it should wait on the cond_wait due to while(1). So why is that `while(!my_data->ipt)` check should be present even when spurious wake ups are taken into consideration? – CHID Oct 29 '12 at 13:41
  • Thank you very much for the link. great info – CHID Oct 29 '12 at 13:42
  • Sorry, I don't really have any insight into your whole application, so I cannot answer this precisely. I think, `my_data->ipt` should be set only by the thread that makes the condition you are waiting for true (i.e. the thread that signals `&mutexaddr_add`). – Maksim Skurydzin Oct 29 '12 at 14:02
  • 1
    Spurious wakeups explain why you use `while ()` instead of `if ()`, but they do not explain why you use `if ()` in place of an unconditional call to `pthread_cond_wait()`. – caf Oct 29 '12 at 14:05
  • Its ok Maxim. I am getting the picture now. thanks to all for helping me out – CHID Oct 29 '12 at 17:22
16

You must test the condition under the mutex before waiting because signals of the condition variable are not queued (condition variables are not semaphores). That is, if a thread calls pthread_cond_signal() when no threads are blocked in pthread_cond_wait() on that condition variable, then the signal does nothing.

This means that if you had one thread set the condition:

pthread_mutex_lock(&m);
cond = true;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);

and then another thread unconditionally waited:

pthread_mutex_lock(&m);
pthread_cond_wait(&c, &m);
/* cond now true */

this second thread would block forever. This is avoided by having the second thread check for the condition:

pthread_mutex_lock(&m);
if (!cond)
    pthread_cond_wait(&c, &m);
/* cond now true */

Since cond is only modified with the mutex m held, this means that the second thread waits if and only if cond is false.

The reason a while () loop is used in robust code instead of an if () is because pthread_cond_wait() does not guarantee that it will not wake up spuriously. Using a while () also means that signalling the condition variable is always perfectly safe - "extra" signals don't affect the program's correctness, which means that you can do things like move the signal outside of the locked section of code.

caf
  • 233,326
  • 40
  • 323
  • 462