A simple counting-semaphore-based reader/writer lock is unfair.
I proposed long ago that we initialize a large number (2000+, up to max long integer) of permits for each lock instance, and require the writer to block to acquire all the permits in one go. As stated on P89 [[Pthreads programming]], such a design is unfair to writer, as a troop of readers can circulate a token among themselves, A passing to B, passing to C, passing to D … and the writer will wait forever even if it arrives on the scene just after A, and before B, C, D… Note the token is not a permit or a lock, just some object represent a “turn”. When Reader Thread A leaves the scene, A releases the Permit, but only After B takes up another Permit. At any moment in time, there’s some permit owned by the readers. The writer is starved unfairly.
One idea is incremental acquisition by writer, who acquires all the free permits ASAP, and waits to grab A’s permit. I feel this is susceptible to deadlock. It also (fairly or unfairly) disables the “shared read” functionality.
A more fair design puts the candidates into some kind of FIFO queue, perhaps a simple linked list. This tentative design uses 2 condition variables (and 2 associated locks). Whenever the RW lock becomes completely free (all permits freed), a signal is sent by the last returning thread, on the rw_free condition. In contrast to the simple design, there’s only one (special) waiting thread on that condition — a messenger thread, who wakes up and then notifies the first thread in the queue, before going back to wait(). In fact, all the queue threads (to be capped at 200) are waiting on the same queue condition, but only the FIFO queue-head thread is allowed to proceed, while others re-enter wait().
A new reader candidate joins the queue if any, otherwise grabs a permit and proceeds.
A new writer candidate joins the queue if any, otherwise determines if lock is completely free. If not free, then it starts the queue. Queue can be started by writer only.
Once a writer leaves the queue, does its job and frees all permits, all the readers before the next writer proceeds to leave the queue.
The queue holds Nodes, each having a pointer to a thread handle — a java Thread instance or a thread id in C. Any thread returning from wait() would check the queue to see if its own thread handle equals the queue-head’s thread handle.
(A tentative design.)