This section details the specific API functions used to manipulate read-write locks. The interface is very similar to the Mutex API you learned in Chapter 7, but split into “read” and “write” operations.
1. The Data Type and Initialization
Just like a mutex uses pthread_mutex_t, a read-write lock uses the datatype pthread_rwlock_t.
Initialization:
- Static: If the variable is static, you can initialize it with a macro:
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;- Dynamic: If allocated at runtime (e.g.,
mallocor shared memory), you must usepthread_rwlock_init(covered in Section 8.3).
2. The Core Functions
The API is divided into functions for locking (Read vs. Write) and unlocking. A. Obtaining a Lock There are two distinct functions to grab the lock, depending on your intent:
pthread_rwlock_rdlock(pthread_rwlock_t *rwptr)- Action: Acquires a read lock.
- Blocking: If a writer currently holds the lock, this function blocks (sleeps) until the writer releases it.
- Concurrency: If other readers hold the lock, this function returns immediately (success), incrementing the reader count.
pthread_rwlock_wrlock(pthread_rwlock_t *rwptr)- Action: Acquires a write lock.
- Blocking: Blocks if any thread holds the lock (either for reading OR writing).
- Exclusivity: Returns only when no one else is touching the protected data.
B. Releasing a Lock
There is only one function to release the lock, regardless of how you acquired it:
pthread_rwlock_unlock(pthread_rwlock_t *rwptr)
- Action: Releases the lock held by the calling thread.
- Intelligence: The system tracks whether you held a read lock or a write lock and updates the internal counters accordingly.
3. Non-Blocking Variants (“Try” Locks)
Just like pthread_mutex_trylock, there are non-blocking versions that return an error (usually EBUSY) instead of putting the thread to sleep if the lock is unavailable:
pthread_rwlock_tryrdlock(...): Tries to get a read lock. ReturnsEBUSYif a writer holds it.pthread_rwlock_trywrlock(...): Tries to get a write lock. ReturnsEBUSYif anyone (reader or writer) holds it.
4. Code Example
Here is a standard pattern for using these locks. Notice how the writers are aggressive (blocking everyone) while readers are permissive (sharing with other readers).
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void *reader_thread(void *arg) {
// 1. Get Read Lock (allows other readers to enter too)
pthread_rwlock_rdlock(&rwlock);
// Critical Section (Reading Only)
read_database();
// 2. Unlock
pthread_rwlock_unlock(&rwlock);
return NULL;
}
void *writer_thread(void *arg) {
// 1. Get Write Lock (No one else allowed in!)
pthread_rwlock_wrlock(&rwlock);
// Critical Section (Modifying)
update_database();
// 2. Unlock
pthread_rwlock_unlock(&rwlock);
return NULL;
}