1. The Data Structure (pthread_rwlock_t)
We need a struct that maintains the state of the lock. We use a standard mutex to protect this struct and two condition variables to put threads to sleep.
typedef struct {
pthread_mutex_t rw_mutex; // Basic lock to protect this struct
pthread_cond_t rw_condreaders; // Condition variable for waiting readers
pthread_cond_t rw_condwriters; // Condition variable for waiting writers
int rw_magic; // Magic number for error checking
int rw_nwaitreaders; // Number of threads waiting to read
int rw_nwaitwriters; // Number of threads waiting to write
int rw_refcount; // -1 if writer has the lock, else # readers holding it
} pthread_rwlock_t;Key Variable: rw_refcount
-1: A Writer has the lock.0: The lock is Free.> 0: The number of Readers currently holding the lock.
2. Obtaining a Read Lock (rdlock)
This function attempts to get shared access.
int pthread_rwlock_rdlock(pthread_rwlock_t *rw) {
int result;
// 1. Acquire the mutex to protect the struct
if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
return(result);
// 2. CHECK CONDITION: Can we read?
// We loop while:
// (a) A writer has the lock (refcount < 0)
// OR
// (b) A writer is WAITING (rw_nwaitwriters > 0) <-- PREFERENCE TO WRITERS
while (rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0) {
rw->rw_nwaitreaders++;
result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
rw->rw_nwaitreaders--; // We woke up, so we aren't waiting anymore
if (result != 0) break; // Error handling
}
// 3. Update State
if (result == 0)
rw->rw_refcount++; // Success! Add ourselves to the reader count.
// 4. Release the internal mutex
pthread_mutex_unlock(&rw->rw_mutex);
return(result);
}Explanation:
- The Critical Check: The line
|| rw->rw_nwaitwriters > 0is the most important part. Even if the lock is currently held by readers (refcount > 0), if a writer is in the queue, this new reader puts itself to sleep. This clears the path for the writer. - The Wait: If we can’t read, we increment
rw_nwaitreaders, sleep onrw_condreaders, and decrement it when we wake up.
3. Obtaining a Write Lock (wrlock)
This function attempts to get exclusive access.
int pthread_rwlock_wrlock(pthread_rwlock_t *rw) {
int result;
// 1. Acquire the mutex
if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
return(result);
// 2. CHECK CONDITION: Can we write?
// We loop while ANYONE has the lock (refcount != 0)
while (rw->rw_refcount != 0) {
rw->rw_nwaitwriters++;
result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
rw->rw_nwaitwriters--;
if (result != 0) break;
}
// 3. Update State
if (result == 0)
rw->rw_refcount = -1; // Success! Mark as locked by writer.
// 4. Release the internal mutex
pthread_mutex_unlock(&rw->rw_mutex);
return(result);
}Explanation:
The writer is very strict. It waits if rw_refcount is anything other than 0. It demands a completely empty lock.
4. Unlocking (unlock)
This function releases the lock. It must be smart enough to know who to wake up (Reader or Writer?).
int pthread_rwlock_unlock(pthread_rwlock_t *rw) {
int result;
// 1. Acquire the mutex
if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
return(result);
// 2. Update the Reference Count
if (rw->rw_refcount > 0)
rw->rw_refcount--; // Released a read lock
else if (rw->rw_refcount == -1)
rw->rw_refcount = 0; // Released a write lock
// 3. WAKEUP LOGIC: Priority to Writers
if (rw->rw_nwaitwriters > 0) {
// If writers are waiting...
if (rw->rw_refcount == 0) {
// ...and the lock is now totally free, wake one writer.
result = pthread_cond_signal(&rw->rw_condwriters);
}
}
else if (rw->rw_nwaitreaders > 0) {
// If NO writers are waiting, wake ALL readers.
result = pthread_cond_broadcast(&rw->rw_condreaders);
}
// 4. Release the internal mutex
pthread_mutex_unlock(&rw->rw_mutex);
return(result);
}Explanation:
- Refcount Logic: It decrements the count. If it was a reader, the count might go from 5 to 4. If it was a writer, it goes from -1 to 0.
- Writer Priority: It first checks
if (rw->rw_nwaitwriters > 0). If a writer is waiting, it tries to wake it up (usingsignal, since only one writer can run). Note: It only wakes the writer ifrefcount == 0. If readers still hold the lock (e.g., count went 5 → 4), it does not wake the writer yet, but it also does not wake any new readers. - Reader Fallback: Only if no writers are waiting (
else if) does it wake up the readers. It usesbroadcastbecause multiple readers can enter simultaneously.