This solution was taken from Advanced Concepts in Operating Systems by Mukesh Singhal and Niranjan G. Shivaratri, pp. 22-24.
readers-writers : monitor; begin readercount : integer; busy : boolean; OKtoread, OKtowrite : condition; procedure startread; begin if busy then OKtoread.wait; readercount := readercount + 1; OKtoread.signal; (* Once one reader can start, they all can *) end startread; procedure endread; begin readercount := readercount - 1; if readercount = 0 then OKtowrite.signal; end endread; procedure startwrite; begin if busy OR readercount != 0 then OKtowrite.wait; busy := true; end startwrite; procedure endwrite; begin busy := false; if OKtoread.queue then OKtoread.signal else OKtowrite.signal; end endwrite; begin (* initialization *) readercount := 0; busy := false; end; end readers-writers;
The above code gives a solution to the reader's priority problem (see Sec. 2.5.3) using monitors. For proper sychronization, reader processes must call the startread procedure before accessing the file (shared resource) and call the endread when the read is finished. Likewise, writer processes must call startwrite before modifying the file and call endwrite when the write is finished. The monitor uses the boolean variable busy to indicate whether a writer is active (i.e., accessing the file) and readercount to keep track of the number of active readers.
On invoking startread, a reader process is blocked and placed in the queue of the OKtoread condition variable if busy is true (i.e., if there is an active writer); otherwise, the reader proceeds and performs the following. The process increments the readercount, and activates a waiting reader, if present, throught the OKtoread.signal operation. On the completion of access, a reader invokes endread, where readercount is decremented. When there are no active readers (i.e., readercount = 0), the last exiting reader process performs the OKtowrite.signal operation to activate any waiting writer.
A writer, on invoking startwrite, proceeds only when no other writer or readers are active. The writer process sets busy to true to indicate that a writer is active. On completion of the access, a writer invokes the endwrite procedure. The endwrite procedure sets busy to false, indicating that no writer is active, and checks the OKtoread queue for the presence of waiting readers. If there is a waiting reader, the exiting writer signals it, otherwise it signals the writer queue. If a reader is activated in endwrite procedure, it increments the readercount and executes the OKtoread.signal, thereby activating the next waiting reader in the queue. This process continues until all the waiting readers have been activated, during which processes trying to enter the monitor are blocked and join the monitor's entry queue. But, after all the readers waiting on the OKtoread condition have been signaled, any newly arrived readers will gain access to the monitor before any waiting writers. In summary, the reader's priority monitor, while not permitting a new writer to start when there is a reader waiting, permits any number of readers to proceed, as long as there is at least one active reader.