98 thread_count, thread[thread_count].increment);
99
100 for (array_count = 0; array_count < ARRAY; array_count++)
101 printf ('%010u ',
102 thread[thread_count].array[array_count]);
103 printf ('
');
104 }
105
106 /*
107 * To be thorough, destroy the barrier.
108 */
109 barrier_destroy (&barrier);
110 return 0;
111 }
7.1.2 Read/write locks
A read/write lock is a lot like a mutex. It is another way to prevent more than one thread from modifying shared data at the same time. But unlike a mutex it distinguishes between
Read/write locks are used to protect information that you need to read frequently but usually don't need to modify. For example, when you build a cache of recently accessed information, many threads may simultaneously examine the cache without conflict. When a thread needs to update the cache, it must have exclusive access.
When a thread locks a read/write lock, it chooses
When both readers and writers are waiting for access at the same time, the readers are given precedence when the write lock is released.
Figure 7.3 shows the operation of a read/write lock being used to synchronize three threads, called thread 1. thread 2, and thread 3. The figure is a sort of timing diagram, with time increasing from left to right. Each of the lines beginning at the labels in the upper left designates the behavior of a specific thread — solid for thread 1, dotted for thread 2, and dashed for thread 3. When the lines drop within the rounded rectangle, they are interacting with the read/write lock. If the

FIGURE 7.3
line drops below the center Line, it shows that the thread has the read/write lock locked, either for exclusive write or for shared read. Lines that hover above the center line represent threads waiting for the lock.
In this example, thread 1 locks the read/write lock for exclusive write. Thread 2 tries to lock the read/write lock for shared read and, finding it already locked for exclusive write, blocks. When thread 1 releases the lock, it awakens thread 2, which then succeeds in locking the read/write lock for shared read. Thread 3 then tries to lock the read/write lock for shared read and, because the read/write lock is already locked for shared read, it succeeds immediately. Thread 1 then tries to lock the read/write lock again for exclusive write access, and blocks because the read/write lock is already locked for read access. When thread 3 unlocks the read/write lock, it cannot awaken thread 1, because there is another reader. Only when thread 2 also unlocks the read/write lock, and the lock becomes unlocked, can thread 1 be awakened to lock the read/write lock for exclusive write access.
The header file rwlock.h and the C source file rwlock.c demonstrate an implementation of read/write locks using standard Pthreads mutexes and condition variables. This is a portable implementation that is relatively easy to understand. One could, of course, create a much more efficient implementation for any specific system based on knowledge of nonportable hardware and operating system characteristics.
The rest of this section shows the details of a read/write lock package. First, rwlock.h describes the interfaces, and then rwlock.c provides the implementation. Part 1 shows the structure of a read/write lock, represented by the type rwlock_t
.
7-9 Of course, there's a mutex to serialize access to the structure. We'll use two separate condition variables, one to wait for read access (called read) and one to wait for write access (called, surprisingly, write), 10 The rwlock_t
structure has a valid member to easily detect common usage errors, such as trying to lock a read/write lock that hasn't been initialized. The member is set to a magic number when the read/write lock is initialized, just as in barrier_init
.
11-12 To enable us to determine whether either condition variable has waiters, we'll keep a count of active readers (r_active) and a flag to indicate an active writer (w_active).
13-14 We also keep a count of the number of threads waiting for read access (r_wait) and for write access (w_wait).
17 Finally, we need a 'magic number' for our valid member. (See the footnote in Section 7.1.1 if you missed this part of the barrier example.)
¦ rwlock.h part 1 rwlock_t
1 #include <pthread.h>
2
3 /*
4 * Structure describing a read/write lock.
5 */
6 typedef struct rwlock_tag {
7 pthread_mutex_t mutex;
8 pthread_cond_t read;
9 pthread_cond_t write;
10 int valid;
11 int r_active;
12 int w_active;
13 int r_wait;
14 int w_wait;
15 } rwlock_t;
16
17 #define RWLOCK VALID 0xfacade
/* wait for read */
/* wait for write */
/* set when valid */
/* readers active */
/* writer active */
/* readers waiting */
/* writers waiting */
We could have saved some space and simplified the code by using a single condition variable, with readers and