Considering this example code:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void *func1(void *);
void *func2(void *);
static pthread_rwlock_t rwLock = PTHREAD_RWLOCK_INITIALIZER;
int main() {
pthread_t thread1;
pthread_t thread2;
pthread_create(&thread1, NULL, func1, NULL);
sleep(1);
int i;
for (i = 0; i < 3; i++) {
pthread_create(&thread2, NULL, func2, (void *)(i + 1));
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}
void *func1(void *arg) {
int j;
for(j = 0; j < 10; j++) {
printf("func 1: trying lock\n");
pthread_rwlock_wrlock(&rwLock);
printf("func 1: lock aquired, sleep 1 sec...\n");
sleep(1);
pthread_rwlock_unlock(&rwLock);
}
}
void *func2(void *arg) {
int true = 1;
while(true) {
pthread_rwlock_rdlock(&rwLock);
printf("func 2: thread %i: lock aquired, sleep 1 sec... \n", (int)arg);
sleep(1);
pthread_rwlock_unlock(&rwLock);
}
}
I have one thread looping over in func1 in which write lock is asked for 1 second, and 3 others looping over in func 2 in which read lock is asked for 1 second.
On the pthread_rwlock_rdlock man page it says "The calling thread acquires the read lock if a writer does not hold the lock and there are no writers blocked on the lock. ". From my output paste on line 5 you can see on "func 1: trying lock" a writer is clearly on block there, so why do my readers will get the lock anyway? After line 5, 3 lines are printed each second. Reducing my reader threads increases writer's chance to get the lock.
func 1: trying lock
func 1: lock aquired, sleep 1 sec...
func 1: trying lock
func 1: lock aquired, sleep 1 sec...
func 1: trying lock
func 2: thread 1: lock aquired, sleep 1 sec...
func 2: thread 3: lock aquired, sleep 1 sec...
func 2: thread 2: lock aquired, sleep 1 sec...
func 2: thread 2: lock aquired, sleep 1 sec...
func 2: thread 3: lock aquired, sleep 1 sec...
func 2: thread 1: lock aquired, sleep 1 sec...
func 2: thread 2: lock aquired, sleep 1 sec...
func 2: thread 3: lock aquired, sleep 1 sec...
func 2: thread 1: lock aquired, sleep 1 sec...
func 2: thread 3: lock aquired, sleep 1 sec...
func 2: thread 1: lock aquired, sleep 1 sec...
func 2: thread 2: lock aquired, sleep 1 sec...
func 2: thread 3: lock aquired, sleep 1 sec...
func 2: thread 2: lock aquired, sleep 1 sec...
func 2: thread 1: lock aquired, sleep 1 sec...
func 2: thread 3: lock aquired, sleep 1 sec...
func 2: thread 1: lock aquired, sleep 1 sec...
func 2: thread 2: lock aquired, sleep 1 sec...
...
Added another example
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define SIZE 10000
void *writerFunc(void *);
void *readerFunc1(void *);
void *readerFunc2(void *);
int setSchedulePolicyTo2(void);
static pthread_rwlock_t rwLock = PTHREAD_RWLOCK_INITIALIZER;
int main() {
pthread_t readerThread1;
pthread_t readerThread2;
pthread_t writerThread;
pthread_create(&readerThread1, NULL, readerFunc1, NULL);
sleep(1);
pthread_create(&readerThread1, NULL, writerFunc, NULL);
sleep(1);
pthread_create(&readerThread2, NULL, readerFunc2, NULL);
pthread_join(readerThread1, NULL);
pthread_join(readerThread2, NULL);
pthread_join(writerThread, NULL);
return 0;
}
void *writerFunc(void *arg) {
printf(" writer's scheduling policy: %d\n", setSchedulePolicyTo2());
printf("writer 1: trying to acquire rw lock...(on hold)\n");
pthread_rwlock_wrlock(&rwLock); // Note ..._wrlock
printf("writer 1: rw lock acquired \n");
pthread_rwlock_unlock(&rwLock);
}
void *readerFunc1(void *arg) {
printf(" reader1's scheduling policy: %d\n", setSchedulePolicyTo2());
printf("reader 1: trying to acquire rw lock...(on hold)\n");
pthread_rwlock_rdlock(&rwLock);
printf("reader 1: rw lock acquired \n");
sleep(3); // enough time to let reader 2 to acquire rw lock before this reader releases it.
pthread_rwlock_unlock(&rwLock);
printf("reader 1: rw lock released \n");
}
void *readerFunc2(void *arg) {
printf(" reader2's scheduling policy: %d\n", setSchedulePolicyTo2());
printf("reader 2: trying to acquire rw lock...(on hold)\n");
pthread_rwlock_rdlock(&rwLock);
printf("reader 2: rw lock acquired \n");
sleep(2);
pthread_rwlock_unlock(&rwLock);
printf("reader 2: rw lock released \n");
}
int setSchedulePolicyTo2() {
struct sched_param sp;
sp.sched_priority = 10;
int policy;
int j;
if((j = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) != 0) {
printf("error: %s \n", strerror(errno));
}
if((j = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
printf("error: %s \n", strerror(errno));
}
return policy;
}
output:
$ gcc main.c -pthread
$ sudo ./a.out
reader1's scheduling policy: 2
reader 1: trying to acquire rw lock...(on hold)
reader 1: rw lock acquired
writer's scheduling policy: 2
writer 1: trying to acquire rw lock...(on hold)
reader2's scheduling policy: 2
reader 2: trying to acquire rw lock...(on hold)
reader 2: rw lock acquired
reader 1: rw lock released
reader 2: rw lock released
writer 1: rw lock acquired
Segmentation fault (end of program)
As per the manpage of pthread_rwlock_rdlock, reader 2 should not acquire the lock because there is writer on hold with same priority and all threads' scheduling policy is set to SCHED_RR (2).
If the Thread Execution Scheduling option is supported, and the threads involved in the lock are executing with the scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not acquire the lock if a writer holds the lock or if writers of higher or equal priority are blocked on the lock; otherwise, the calling thread shall acquire the lock.
The writer acquires the lock only when both readers have released the rw lock.