10 status = pthread_mutex_lock (&rwl->mutex);
11 if (status != 0)
12 return status;
13 if (rwl->w_active) {
14 rwl->r_wait++;
15 pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
16 while (rwl->w_active) {
17 status = pthread_cond_wait (&rwl->read, &rwl- >mutex);
18 if (status != 0)
19 break;
20 }
21 pthread_cleanup_pop (0);
22 rwl->r_wait--;
23 }
24 if (status == 0)
25 rwl->r_active++;
26 pthread_mutex_unlock (&rwl->mutex);
27 return status;
28 }
Part 5 shows rwl_readtrylock. This function is nearly identical to rwl_readlock, except that, instead of waiting for access if a writer is active, it returns EBUSY. It doesn't need a cleanup handler, and has no need to increase the count of waiting readers.
This function must also be modified to implement 'writer preference' read/ write locks, by returning EBUSY when a writer is waiting, not just when a writer is active.
¦ rwlock.c part 5 rwl_readtrylock
1 /*
2 * Attempt to lock a read/write lock for read access (don't
3 * block if unavailable).
4 */
5 int rwl_readtrylock (rwlock_t *rwl)
6 {
7 int status, status2;
8
9 if (rwl->valid != RWLOCK_VALID)
10 return EINVAL;
11 status = pthread_mutex_lock (&rwl->mutex);
12 if (status != 0)
13 return status;
14 if (rwl->w_active)
15 status = EBUSY;
16 else
17 rwl->r_active++;
18 status2 = pthread_mutex_unlock (&rwl->mutex);
19 return (status2 != 0 ? status2 : status);
20 }
13 Part 6 shows rwl_readunlock
. This function essentially reverses the effect of rwl_readlock
or rwl_tryreadlock
, by decreasing the count of active readers (r_active).
14-15 If there are no more active readers, and at least one thread is waiting for write access, signal the write condition variable to unblock one. Note that there is a race here, and whether you should be concerned about it depends on your notion of what should happen. If another thread that is interested in read access calls rwl_readlock
or rwl_tryreadlock
before the awakened writer can run, the reader may 'win,' despite the fact that we just selected a writer.
Because our version of read/write locks has 'reader preference,' this is what we usually want to happen — the writer will determine that it has failed and will resume waiting. (It received a spurious wakeup.) If the implementation changes to prefer writers, the spurious wakeup will not occur, because the potential reader would have to block. The waiter we just unblocked cannot decrease w_wait until it actually claims the lock.
¦ rwlock.c part 6 rwl_readunlock
1 /*
2 * Unlock a read/write lock from read access.
3 */
4 int rwl_readunlock (rwlock_t *rwl)
5 {
6 int status, status2;
7
8 if (rwl->valid != RWLOCK_VALID)
9 return EINVAL;
10 status = pthread_mutex_lock (&rwl->mutex);
11 if (status != 0)
12 return status;
13 rwl->r_active--;
14 if (rwl->r_active == 0 && rwl->w_wait > 0)
15 status = pthread_cond_signal (&rwl->write);
16 status2 = pthread_mutex_unlock (&rwl->mutex);
17 return (status2 == 0 ? status : status2);
18 }
13 Part 7 shows rwl_writelock
. This function is much like rwl_readlock
, except for the predicate condition on the condition variable wait. In part 1, I explained that, to convert from 'preferred read' to 'preferred write,' a potential reader would have to wait until there were no active or waiting writers, whereas currently it waits only for active writers. The predicate in rwl_writelock is the converse of that condition. Because we support 'preferred read,' in theory, we must wait here if there are any active or waiting readers. In fact, it is a bit simpler, because if there are any active readers, there cannot be any waiting readers—the whole point of a read/write lock is that multiple threads can have read access at the same time. On the other hand, we do have to wait if there are any active writers, because we allow only one writer at a time.
25 Unlike r_active, which is a counter, w_active is treated as a boolean. Or is it a counter? There's really no semantic difference, since the value of 1 can be considered a boolean TRUE or a count of 1 — there can be only one active writer at any time.
¦ rwlock.c_part 7 rwl_writelock
1 /*
2 * Lock a read/write lock for write access.
3 */
4 int rwl_writelock (rwlock_t *rwl)
5 {
6 int status;
7
8 if (rwl->valid != RWLOCK_VALID)
9 return EINVAL;