33

pthread question:

it appears that a condition variable only works if pthread_cond_wait is called before the other thread calls pthread_cond_notify. If notify somehow happens before wait then wait will be stuck.

My question is: when should condition variables be used?

The scheduler can preempt threads and a notify may happen before wait.

Waiting on semaphores does not have this problem -- these have a counter.

When is a conditional variable better than a semaphore?

Here is a test:

File condvar.c

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

// test of conditional variables;
// if cond-var is notified before wait starts, then wait never wakes up !!!
// better to use semaphores than this crap.

pthread_mutex_t cond_var_lock =  PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;

int wait_first = 1;

void *tfunc(void *arg)
{
    (void) arg;

    if (!wait_first)
        sleep(1);

    fprintf(stderr,"on enter cond_var_lock %lx\n", pthread_self());
    pthread_mutex_lock( &cond_var_lock);
    fprintf(stderr,"before pthread_cond_wait %lx\n", pthread_self());
    pthread_cond_wait( &cond_var, &cond_var_lock);
    fprintf(stderr,"after pthread_cond_wait %lx\n", pthread_self());
    pthread_mutex_unlock( &cond_var_lock);
    fprintf(stderr,"after exit cond_var_lock %lx\n", pthread_self());

    return 0;
}

int main(int argc, char *argv[])
{
    pthread_t th;

    if (argc > 0) 
    wait_first = atoi( argv[1] );

    if (wait_first)
    {
        fprintf(stderr,"********* Wait first ***********\n");
    } else {
        fprintf(stderr,"********* Notify first *********\n");
    }


    pthread_create( &th, 0, tfunc, 0 );

    if (wait_first)
    {
        sleep(1);
    } 

    fprintf(stderr, "! on enter cond_var_lock %lx\n", pthread_self());
    pthread_mutex_lock( &cond_var_lock);
    fprintf(stderr, "! before pthread_cond_signal %lx\n", pthread_self());
    pthread_cond_signal( &cond_var );
    fprintf(stderr, "! after pthread_cond_signal %lx\n", pthread_self());
    pthread_mutex_unlock( &cond_var_lock);
    fprintf(stderr, "! after exit cond_var_lock %lx\n", pthread_self());

    sleep(5);
    return 0;    
}

File test.sh

#!/bin/sh

set -e
set -x

gcc condvar.c -o condvar -lpthread

./condvar 1

./condvar 0

Test output

Output:

+ gcc condvar.c -o condvar -lpthread
+ ./condvar 1
********* Wait first ***********
on enter cond_var_lock b7779b70
before pthread_cond_wait b7779b70
! on enter cond_var_lock b777a6c0
! before pthread_cond_signal b777a6c0
! after pthread_cond_signal b777a6c0
! after exit cond_var_lock b777a6c0
after pthread_cond_wait b7779b70
after exit cond_var_lock b7779b70
+ ./condvar 0
********* Notify first *********
! on enter cond_var_lock b785c6c0
! before pthread_cond_signal b785c6c0
! after pthread_cond_signal b785c6c0
! after exit cond_var_lock b785c6c0
on enter cond_var_lock b785bb70
before pthread_cond_wait b785bb70
jww
  • 97,681
  • 90
  • 411
  • 885
MichaelMoser
  • 3,172
  • 1
  • 26
  • 26
  • to add: iIf you run on a system with older glibc (before 2.5) then semaphores have a race conditions and sometimes cause memory corruption https://sourceware.org/bugzilla/show_bug.cgi?id=12674 – MichaelMoser Sep 24 '17 at 11:24

1 Answers1

62

Condition variables should be used as a place to wait and be notified. They are not the condition itself and they are not events. The condition is contained in the surrounding programming logic. The typical usage pattern of condition variables is

// safely examine the condition, prevent other threads from
// altering it
pthread_mutex_lock (&lock);
while ( SOME-CONDITION is false)
    pthread_cond_wait (&cond, &lock);

// Do whatever you need to do when condition becomes true
do_stuff();
pthread_mutex_unlock (&lock);

On the other hand, a thread, signaling the condition variable, typically looks like

// ensure we have exclusive access to whathever comprises the condition
pthread_mutex_lock (&lock);

ALTER-CONDITION

// Wakeup at least one of the threads that are waiting on the condition (if any)
pthread_cond_signal (&cond);

// allow others to proceed
pthread_mutex_unlock (&lock)
chill
  • 16,470
  • 2
  • 40
  • 44
  • How does that solve the following problem? if pthread_cond_notify is called before pthread_cond_wait, then wait will be stuck; – MichaelMoser Dec 25 '13 at 12:24
  • 5
    If you call `pthread_cond_wait` that means the condition is false. It cannot change in the meantime, because it's protected by the mutex. Block in the condition and the corresponding unlock of the mutex are atomic. – chill Dec 25 '13 at 12:28
  • 2
    In the first block, you can also call do_stuff(); after pthread_mutex_unlock (&lock); – Lenny Apr 20 '17 at 17:33
  • 1
    @Lenny: No. `SOME-CONDITION` may change at any time between the unlock and the beginning of `do_stuff()`, which will violate the invariant that `do_stuff` begins executing if, and only if `SOME-CONDITION` is true. – chill Apr 20 '17 at 18:00
  • 1
    @chill Years later... I am trying to understand conditional variables myself. I think that the "while ( SOME-CONDITION is false)" should be "if ( SOME-CONDITION is false)" because condition variables are used precisely to avoid the while loops to wait. Am I wrong? – Sunny Mar 16 '18 at 05:07
  • 3
    @Sam, it is possible to return from the `pthread_cond_wait` call with the condition still (or again!) false. Hence it should be tested again, hence the loop. That's fine, it's still not a busy-wait, polling loop. :) – chill Mar 16 '18 at 07:40
  • @chill So it keeps looping until the condition is false at which time it runs pthread_cond_wait. I see the use of 'while' in all examples on the net but is the looping not a waste of CPU cycles. I thought that condition variables are intended to prevent this. I am missing something here... Possibly something simple. – Sunny Mar 16 '18 at 10:02
  • @chill. Please ignore previous comment. Understood! – Sunny Mar 16 '18 at 11:18
  • @Sunny the `pthread_cond_wait` is just a hint to the OS this thread should be waiting, but nothing prevents the OS from waking it up even though no signals has been sent. It's a security. – ancyrweb Dec 22 '21 at 06:46