Thread 1 signals the condition variable, which has no effect since there are no waiters. Thread 1 then waits on the condition variable. Thread 2 also blocks on the condition variable and, shortly thereafter, thread 3 signals the condition variable. Thread 3's signal unblocks thread 1. Thread 3 then waits on the condition variable. Thread 1 broadcasts the condition variable, unblocking both thread 2 and thread 3. Thread 3 waits on the condition variable shortly thereafter, with a timed wait. Some time later, thread 3's wait times out, and the thread awakens.
3.3.1 Creating and destroying a condition variable
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init (pthread_cond_t *cond,
pthread_condattr_t *condattr);
int pthread_cond_destroy (pthread_cond_t *cond);
A condition variable is represented in your program by a variable of type pthread_cond_t. You should never make a copy of a condition variable, because the result of using a copied condition variable is undefined. It would be like telephoning a disconnected number and expecting an answer. One thread could, for example, wait on one copy of the condition variable, while another thread signaled or broadcast the other copy of the condition variable—the waiting thread would not be awakened. You can, however, freely pass pointers to a condition variable so that various functions and threads can use it for synchronization.
Most of the time you'll probably declare condition variables using the extern or static storage class at file scope, that is, outside of any function. They should have normal (extern) storage class if they are used by other files, or static storage class if used only within the file that declares the variable. When you declare a static condition variable that has default attributes, you should use the PTHREAD_COND_INITIALIZER initialization macro, as shown in the following example, cond_static.c.
■ cond_static.c
1 #include
2 #include "errors.h"
3
4 /*
5 * Declare a structure, with a mutex and condition variable,
6 * statically initialized. This is the same as using
7 * pthread_mutex_init and pthread_cond_init, with the default
8 * attributes.
9 */
10 typedef struct my_struct_tag {
11 pthread_mutex_t mutex; /* Protects access to value */
12 pthread_cond_t cond; /* Signals change to value */
13 int value; /* Access protected by mutex */
14 } my_struct_t;
15
16 my_struct_t data = {
17 PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
18
19 int main (int argc, char *argv[])
20 {
21 return 0 ;
22 }
Condition variables and their predicates are "linked"—for best results, treat them that way!
When you declare a condition variable, remember that a condition variable and the associated predicate are "locked together." You may save yourself (or your successor) some confusion by always declaring the condition variable and predicate together, if possible. I recommend that you try to encapsulate a set of invariants and predicates with its mutex and one or more condition variables as members in a structure, and carefully document the association.
Sometimes you cannot initialize a condition variable statically; for example, when you use malloc to create a structure that contains a condition variable. Then you will need to call pthread_cond_init to initialize the condition variable dynamically, as shown in the following example, cond_dynamic.c. You can also dynamically initialize condition variables that you declare statically—but you must ensure that each condition variable is initialized before it is used, and that each is initialized only once. You may initialize it before creating any threads, for example, or by using pthread_once (Section 5.1). If you need to initialize a condition variable with nondefault attributes, you must use dynamic initialization (see Section 5.2.2).
■ cond_dynamic.c
1 #include
2 #include "errors.h" 3
4 /*
5 * Define a structure, with a mutex and condition variable.
6 */
7 typedef struct my_struct_tag {