Friday, January 31, 2014

pthread - conditional variable

Conditional variables are like flags.
 
When you want to wake up some threads when some condition is met, conditional variables are used. It is used with mutex always, since the conditional variable itself is protected by the mutex.
 
  • int pthread_cond_init( pthread_cond_t * cond, pthread_condattr_t * cond_attr );
        cond is initialized with cond_attr. When NULL, default is set. It can be done as follows too.
            pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • int pthread_cond_destroy ( pthread_cond_t * cond );
            It will destroy the variable.
  • int pthread_cond_wait( pthread_cond_t * cond, pthread_mutex_t * mutex);
            It suspends the thread. It unlocks the mutex. It has to be waken-up pthread_cond_signal or pthread_cond_broadcast is necessary. When it wake-up, again it locks the mutex.
  • int pthread_cond_timedwait( pthread_cond_t * cond, pthread_mutex_t *mutex, const struct timespec * abstime );
            It is same as pthread_cond_wait in addition to timeout functionality.

  • int pthread_cond_signal( pthread_cond_t * cond );
        It wakes-up one of the threads waiting. If sheduling policy is not set, it is not decided which will be woke-up.
  • int pthread_cond_broadcast( pthread_cond_t * cond );
        All waiting tasks will be woken-up.


One Object or Class which needs to be protected by single lock. But, both producer and consumer threads will try to access the object. If both do not have any condition for their execution, then there are no issues. If they are depend on each other, like the consumer needs to object to have the count not to be zero, the consumer has some condition on its execution. Similarly, the producer will have the condition the queue needs to have some slots. In such a case, if any one of them holds the lock and waits for others to satisfy the condition, then the others cannot do simply because the lock is hold by the waiting one. So, release the key just before waiting.

Assume accessing a toilet, but there is no toilet paper. Then, leave the key and wait on the bench. Let the attender use the key to fill the paper. If use spinlock, you cannot wait, take the key, check the paper, leave the key continuously till it is getting filled. Attender also will take the key, fill the paper, leave the key till it is getting emptied. If there is paper, then no issues, just keep on hold the key, finish your job and leave the key, come out.

Same mutex can be used on multiple condition variables, but not the vice-versa. For a single variable "size of queue" protected by a single mutex, there can be two conditions that "wait for empty space in the queue" and "wait for any element in the queue". So, two different conditional variables associated with same mutex. But, there is no sense that two mutexes are used for one condition variable.

If more than one predicate condtions are associated with single condition variable, then "broadcast" will wake-up all threads. The threads for which the condition became true will continue executions. The wrong ones will go to sleep again. However, it represents a weaker condition than the conditions being checked by individual threads.
  
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;

int main( int argc, char ** argv ) {
        :
    pthread_cond_broadcast( &cond );
    return 0;
}


void * temp_func( void * arg ) {
    pthread_mutex_lock( &mutex );
    while (!p) { // While the condition/predicate/assertion that we are waiting for is not true...
         pthread_cond_wait( &cond, &mutex ); // Wait on this monitor's lock and condition variable.
    }

     // Do critical processing
    pthread_mutex_unlock( &mutex ) ;
    return 0;
}

 

No comments: