Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read Write lock implementation in C++

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?

like image 528
user1918858 Avatar asked Feb 23 '16 21:02

user1918858


People also ask

Can a thread hold a reader lock or a writer 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.

What is the problem with the readerwriterlock class?

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.

What happens if you call releasewriterlock or releasereaderlock?

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.

How do I release a lock from a thread?

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.


2 Answers

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:

  • as long as readers are active, the writer is blocked by the unique_lock waiting for the mutex to be aquirable in exclusive mode.
  • however as long as the wrtier is waiting, new readers can obtain access to the shared lock, causing the unique_lock to be further delayed.

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 ?

like image 194
Christophe Avatar answered Sep 25 '22 11:09

Christophe


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.

like image 36
systemcpro Avatar answered Sep 22 '22 11:09

systemcpro