This is homework of my operating system course. Understanding the basic principle of Reader-Writer lock is not difficult. It is Rust that causes me to spend days working on it.
RWLock allows multiple readers threads to simultaneously access the data. But only one writer is allowed to do so at any one time. As usual, to writer such a lock, we need a lock (maybe more) and conditional variables.
What are so special of this lock that we need to consider
carefully ? What are the variables and data structures we may use to implement it ?
- Firstly, we need to consider when the readers/writers should wait. For a reader, it is definitely not allowed to read the data if there is some active writer modifying the shared data and vice versa.
- Secondly, we have to think about how to wake up other threads when the current reader/writer is done. In this homework we are required to implement reader-prefered/writer-prefered and FIFO/LIFO. By the way, the rwlock in std is not so complicated.
- Finally, to those who are not familiar with Rust yet, it will be suffering to write it.
The structure of RWLock is,
mutex provides the exclusive access of RwLock. We need this because we don’t want to be disturbed when modify something.
data is what the reader and writer are looking for. We use UnsafeCell because we need interior mutability. Read this if you want to learn more.
state_vars means state variables that including the waiting_writers/readers and active_writers/reader currently. In order to implement FIFO/LIFO, we give each thread a conditional variable that is used to wake the thread up in the future according to FIFO/LIFO. For instance, if I want to wake the first write up, which is waiting because readers are reading, we just call
waiting_writers.notify_one(). Then the thread blocked on this conditional variable will continue and decide to move on or still keep on waiting.
The two enums,
It is easy to write
read_should_wait() based on the annotation in Preference. I won’t bother to elaborate on this.
After we gain the lock, we have the exclusive access to the RWLock. Firstly, we need a new conditional variable as explained before. We put it on the heap to make sure it is alive. And we put the pointer into a Rc because we have to push it into a vector. If we don’t use a Rc, after push cv into the vector, we can’t access cv anymore. Don’t forget to pop cv out!
We implement the Drop trait for RWLockReadGuard/RWLockWriteGuard, which is just a reference of the RWLock.
The main part is wakeup function. It is not so hard.
See the source code here