- Forward


Notifying Threads of State Changes
in Pthreads


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu

Print

Review
Back SMYC Forward
  • Threads:
    • A mechanism that permits a single process to perform multiple tasks concurrently
  • Shared Variables:
    • Threads share the initialized data, uninitialized data, and heap segments
    • Threads share signal dispositions
The Good and the Bad
Back SMYC Forward
  • The Good:
    • Because threads share the "global" data segments it is easy for them to share information
  • The Bad:
    • Because threads share signal dispositions it is very hard to use signals to have one thread notify another that a shared variable has changed value
A Possible Remedy
Back SMYC Forward
  • The General Idea:
    • Have one thread poll (i.e., repeatedly check, perhaps with a small delay to see if the variable has changed)
  • The Downsides:
    • Polling wastes CPU time
A Possible Remedy (cont.)
Back SMYC Forward
unixexamples/conditionvariables/tax1.c
 
Two Observations
Back SMYC Forward
  • Polling:
    • Must ensure that access is mutually exclusive (e.g., using a mutex)
  • Any Other Mechanism:
    • Is likely to have the same requirement
A Good Mechanism for Indicating State Changes
Back SMYC Forward
  • The Mechanism:
    • pthread_cond_t variables
  • Actions:
    • Block until notified
    • Unblock a single (arbitrary) thread
    • Unblock all threads
Creating Condition Variables
Back SMYC Forward
  • Step 1 - Allocation:
    • Allocate memory for the condition variable either statically in the initialized data segment, automatically (on the stack), or dynamically (on the heap)
  • Step 2 - Initialization:
    • Assign attributes to the condition variable
Creating Condition Variables (cont.)
Back SMYC Forward
  • Static Allocation and Initialization:
    • static pthread_cond_t example = PTHREAD_COND_INITIALIZER
  • Dynamic Initialization:
    • Must be used for condition variables that are dynamically allocated (on the heap) or automatically allocated (on the stack)
    • Can be used for condition variables that are statically allocated when non-default attributes are needed
The Pattern
Back SMYC Forward
  • Entities:
    • Each condition variable has a predicate (i.e., a Boolean-valued expression) involving one or more associated shared variables
    • Each condition variable has an associated mutex
  • Threads:
    • The blocking thread(s)
    • The notifying thread(s)
The Pattern (cont.)
Back SMYC Forward
  • The Blocking Thread:
    • acquire the mutex while (predicate) block on the condition variable when notified use the shared variable relinquish the mutex
  • The Notifying Thread:
    • acquire the mutex use the shared variable relinquish the mutex notify
The Blocking Thread in Detail
Back SMYC Forward
  • Setup:
    • Obtain the mutex
    • While the shared variable is in the wrong state, atomically relinquish the mutex and block on the condition variable
  • Upon Notification:
    • Obtain the mutex
    • Use the shared variable
    • Relinquish the mutex
Blocking: pthread_cond_wait() pthread_cond_wait
Back SMYC Forward
int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
Purpose:
Wait/block on a condition variable
Details:
cv The condition variable to wait for
mutex The mutex associated with the condition variable
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h

When Called: mutex is relinquished and the calling thread is blocked.

When Unblocked: mutex is obtained (so that the shared variable can be used).

Blocking (cont.)
Back SMYC Forward
  • Why Use a Loop?
    • pthread_cond_wait() returns when notified but there is no guarantee that the predicate is satisfied so it must be re-checked
  • Why Wouldn't the Predicate be Satisfied?
    • Other threads may have been notified, acquired the mutex first and used the shared variables
    • Spurious notification (though rare) can occur (and are permitted for efficiency reasons)
Notification
Back SMYC Forward
  • Notifying A Single (Arbitrary) Blocked Thread:
    • Is more efficient
    • Is only appropriate when it doesn't matter which thread is notified (e.g., when all of the blocking threads perform the same task)
  • Notifying All Blocking Threads:
    • Is less efficient
    • Should always yield correct results since all blocking threads should be able to handle redundant and/or spurious notifications
Notification (cont.): pthread_cond_signal() pthread_cond_signal
Back SMYC Forward
int pthread_cond_signal(pthread_cond_t *cv)
Purpose:
Notify a single (arbitrary) thread that is blocking on a condition variable
Details:
cv The condition variable the threads are/may be blocking on
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h
Notification (cont.): pthread_cond_broadcast() pthread_cond_broadcast
Back SMYC Forward
int pthread_cond_broadcast(pthread_cond_t *cv)
Purpose:
Notify all threads that are blocking on a condition variable
Details:
cv The condition variable the threads are/may be blocking on
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h

Note: All blocking threads will be notified but which one(s) are scheduled is arbitrary.

An Example
Back SMYC Forward
unixexamples/conditionvariables/tax2.c
 
Dynamic Initialization: pthread_cond_init() pthread_cond_init
Back SMYC Forward
int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *attr)
Purpose:
Initialize a condition variable
Details:
cv The condition variable to be initialized
attr The attributes to use (of NULL for the default attributes)
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h
Destruction
Back SMYC Forward
  • Which Condition Variables?
    • Those that are automatically or dynamically allocated
  • When?
    • When no threads are waiting on it
    • Before freeing the memory (for dynamically allocated) or before the "host function" returns (for automatically allocated)
Destruction (cont.): pthread_cond_destroy() pthread_cond_destroy
Back SMYC Forward
int pthread_cond_destroy(pthread_cond_t *cv)
Purpose:
Indicate that a condition variable will no longer be used
Details:
cv The condition variable to be destroyed
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h
There's Always More to Learn
Back -