使用Linux的Mutex,实现一个FCFS Error-Checking的读写锁。
Requirement
In this lab, you are asked to implement a First-Come-First-Serve (FCFS) Error-Checking Read/Write Lock using pthread mutexes and condition variables.
Overview
An RW lock allows concurrent access for read-only operations, while write operations require exclusive access. This means that multiple threads can read the data in parallel but an exclusive lock is needed for writing or modifying data. When a writer is writing the data, all other writers or readers will be blocked until the writer is finished writing.
By a First-Come First-Server (FCFS) Read/Write lock, we mean that when a writer thread (a thread who wants a write lock) is blocked because one or more threads currently hold read locks, new requests for read locks should also be blocked and be served only after the writer thread has obtained and released the write lock.
By an Error-Checking Read/Write lock, we mean that your implementation should return an error code when a thread who tries to obtain a lock already has read or write lock, and a thread who calls read or write unlock does not hold the corresponding type of lock. When such an error situation occurs, your thread should return without having to wait for any other thread calling unlock.
In your implementation, you are only allowed to use pthread mutexes and pthread condition variables for synchronization primitives. Usage of semaphores, read/write locks, and other synchronization primitives is not allowed.
Goal
You should implement the following C++ class.
1 | class RWLock { |
Grading
Ten test cases are provided with the lab. Expected output are also provided. Your implementation should generate identical output as the given output on corresponding test cases. Your submission receives 10% of lab 4 grade for each test case that your implementation is able to provide identical output. No new test cases will be used in grading.
Provided Files.
You are provided with the following files:
- src/Makefile Do not edit.
- src/TestRWLock.cc Do not edit. Code for testing implementation of RWLock.
- src/script.h Do not edit. Script of last test case.
- src/RWLock.h Edit to add private fields and methods.
- src/RWLock.cc Edit to replace the current trivial implementation.
- test/out01.txt to test/out10.txt Expected output for the 10 test cases, generated by running TestRWLock against the reference implementation.
Generate executable lab4. Run lab4 n > tmp.txt
, where n is from 1 to 10. Then run diff tmp.txt outn.txt to see whether your output is identical as desired output.
APIs to Use
You are allowed to use pthread_mutex
and pthread_cond_var
. No other synchronization primitives are allowed. If you want to use add any header files other than the ones already included in the source file, please check with the instructor first.
Recommended Steps to Take
Understand the implementation and the questions. This implementation of RWLock there uses two mutexes m1, m2, and a semaphore sem. The mutex m2 ensures that accessing internal data of the lock is atomic. The mutex m1 is used to enable a waiting writer thread to block all later reader threads (so as to achieve FCFS). The semaphore is used to block out writers when readers are holding the lock. Note that this implementation uses a semaphore (which is not allowed in this lab), is not error checking, and does not support all required functions.
Second, implement your RWLock using the code. This should suffice to pass test cases 1 to 4. After this is done, replace the use of semaphore with a condition variable, and make sure that you can pass test cases 1 to 4.
Third, add the logic to handle error checking. To do this, you need to use the following pthread APIs.
1 | pthread_t pthread_self(void); |
returns the ID of the calling thread.
1 | int pthread_equal(pthread_t t1, pthread_t t2); |
If the two thread IDs are equal, pthread_equal() returns anonzero value; otherwise, it returns 0.
You need to remember which thread(s) are holding the lock, and perform checks inside each function. Note that you should always use pthread_equal to compare two thread ids. It is required that if an error occurs, the function should return without waiting for another thread to call unlock. This means that while it is okay to block on the mutex m2 while performing error checks, your implementation should not block on m1 or the condition variable (semaphore in the sample implementation). Whenever m2 is not available, it means that another thread is in critical section, and given a little bit more time, m2 will become available. However, waiting on m1 or the condition variable (or semaphore) means waiting for some other threads call certain unlock methods, which may never happen.
Hint. It is okay to separate the logic for error checking and the logic to obtain a lock so that other threads get interleaved between the two steps. The error status depends only on the current thread and cannot be changed by other threads actions.
After adding error checking logic, you should be able to pass test case 5 and 6.
Fourth, add the logic for handling try_read_lock()
, try_write_lock()
, and write_to_read()
.