I am trying to use read/write lock in C++ using shared_mutex
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
class Test {
Lock lock;
WriteLock writeLock;
ReadLock readLock;
Test() : writeLock(lock), readLock(lock) {}
readFn1() {
readLock.lock();
/*
Some Code
*/
readLock.unlock();
}
readFn2() {
readLock.lock();
/*
Some Code
*/
readLock.unlock();
}
writeFn1() {
writeLock.lock();
/*
Some Code
*/
writeLock.unlock();
}
writeFn2() {
writeLock.lock();
/*
Some Code
*/
writeLock.unlock();
}
}
The code seems to be working fine but I have a few conceptual questions.
Q1. I have seen the recommendations to use unique_lock and shared_lock on http://en.cppreference.com/w/cpp/thread/shared_mutex/lock, but I don't understand why because shared_mutex already supports lock and lock_shared methods?
Q2. Does this code have the potential to cause write starvation? If yes then how can I avoid the starvation?
Q3. Is there any other locking class I can try to implement read write lock?
A thread can hold a reader lock or a writer lock, but not both at the same time. Instead of releasing a reader lock in order to acquire the writer lock, you can use UpgradeToWriterLock and DowngradeFromWriterLock. In some situations, you might find that you're holding a reader lock and you need to upgrade to a writer lock.
One more problem with the ReaderWriterLock class is that it allows Reader threads to acquire writer locks. If you set an infinite timeout, it will create a deadlock situation, where the thread just waits to get the Writer lock but can't because the very same thread holds on to the Reader lock and is yet to release it.
If you subsequently call ReleaseWriterLock or ReleaseReaderLock, the runtime will throw an exception. A thread can hold a reader lock or a writer lock, but not both at the same time. Instead of releasing a reader lock in order to acquire the writer lock, you can use UpgradeToWriterLock and DowngradeFromWriterLock.
If a thread obtains a reader lock, then it must call ReleaseReaderLock to release the lock. Similarly, any call to AcquireWriterLock must be balanced by a call to ReleaseWriterLock. If you fail to release the locks, your thread will keep the resource locked and other threads will not be able to access it.
Q1: use of a mutex wrapper
The recommendation to use a wrapper object instead of managing the mutex directly is to avoid unfortunate situation where your code is interrupted and the mutex is not released, leaving it locked forever.
This is the principle of RAII.
But this only works if your ReadLock or WriteLock are local to the function using it.
Example:
readFn1() {
boost::unique_lock< Lock > rl(lock);
/*
Some Code
==> imagine exception is thrown
*/
rl.unlock(); // this is never reached if exception thrown
} // fortunately local object are destroyed automatically in case
// an excpetion makes you leave the function prematurely
In your code this won't work if one of the function is interupted, becaus your ReadLock WriteLock object is a member of Test and not local to the function setting the lock.
Q2: Write starvation
It is not fully clear how you will invoke the readers and the writers, but yes, there is a risk:
If you want to avoid starvation, you have to ensure that waiting writers do get the opportunity to set their unique_lock. For example att in your readers some code to check if a writer is waiting before setting the lock.
Q3 Other locking classes
Not quite sure what you're looking for, but I have the impression that condition_variable
could be of interest for you. But the logic is a little bit different.
Maybe, you could also find a solution by thinking out of the box: perhaps there's a suitable lock-free data structure that could facilitate coexistance of readers and writers by changing slightly the approach ?
The types for the locks are ok but instead of having them as member functions create then inside the member functions locktype lock(mymutex)
. That way they are released on destruction even in the case of an exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With