2

I am currently implementing a read/write lock using boost library, without using shared_lock and unique_lock. I have already read some related questions (e.g., How would a readers/writer lock be implemented in C++11?), but I still want to optimize the implementation.

Here is my code:

enum LockType { NO_LOCK, READ_LOCK, WRITE_LOCK, INC_LOCK };
boost::mutex mutex_;
boost::condition condition_;
LockType lock_;
size_t owner_count_;

void AcquireReadLock() {
    mutex_.lock();
    while (lock_ != NO_LOCK && lock_ != READ_LOCK){
        condition_.wait(mutex_);
    }
    // if there is no lock, then acquire read lock.
    if (lock_ == NO_LOCK) {
        lock_ = READ_LOCK;
        ++owner_count_;
        mutex_.unlock();
        return;
    }
    else {
        // if there is read lock, then still acquire read lock.
        assert(lock_ == READ_LOCK);
        ++owner_count_;
        mutex_.unlock();
        return;
    }
}

void AcquireWriteLock() {
    mutex_.lock();
    while (lock_ != NO_LOCK){
        condition_.wait(mutex_);
    }
    // if there is no lock, then acquire write lock.
    assert(lock_ == NO_LOCK);
    lock_ = WRITE_LOCK;
    mutex_.unlock();
    return;
}

void ReleaseReadLock() {
    mutex_.lock();
    --owner_count_;
    if (owner_count_ == 0) {
        lock_ = NO_LOCK;
    }
    mutex_.unlock();
    // is it correct to use notify_all?
    condition_.notify_all();
}

void ReleaseWriteLock() {
    mutex_.lock();
    lock_ = NO_LOCK;
    mutex_.unlock();
    // is it correct to use notify_all?
    condition_.notify_all();
}

The problem is:

  1. whether I should use notify_all when releasing the lock? According to the document, once a thread gets notified, it will reacquire the lock. If using notify_all, then multiple threads can reacquire the same lock. What will happen then? And whether will a thread acquire the lock before checking the condition (i.e., lock_!=NO_LOCK && lock_!=READ_LOCK)?

  2. how can I optimize the program? obviously, when releasing a read lock, we only need to notify the threads that attempt to acquire the write lock, since read never blocks read. So how to implement this idea?

Thanks in advance for your kind help!

Community
  • 1
  • 1
Frank
  • 37
  • 3

2 Answers2

1
  1. whether I should use notify_all when releasing the lock? According to the document, once a thread gets notified, it will reacquire the lock. If using notify_all, then multiple threads can reacquire the same lock. What will happen then? And whether will a thread acquire the lock before checking the condition (i.e., lock_!=NO_LOCK && lock_!=READ_LOCK)?

Yes, you should use notify_all when releasing the lock. All condition_ waiting for the mutex_ will be waked one by one for they must lock the mutex_ firstly(this is done inner the condition_ wait operation).

  1. how can I optimize the program? obviously, when releasing a read lock, we only need to notify the threads that attempt to acquire the write lock, since read never blocks read. So how to implement this idea?

All threads waiting for mutex_ must be notified, for some writing threads may waiting for the read lock to be released.

I hope this will help you!

yanchong
  • 276
  • 1
  • 9
  • hi yanchong, I think your answer is in contradictory to @Tsyvarev 's, as he thinks that **notify** is enough. It seems reasonable that we only need to notify one thread, and the notified thread will check the condition and acquire the lock before entering the critical section. Is it correct? – Frank May 09 '15 at 05:31
  • @Frank, in your `ReleaseReadLock()` when notify is called, your _mutex is unlocked, so there may another write thread just `AcquireWriteLock` succefully, after that some read threads then blocked in `AcquireReadLock`, as a result before your notify is called there are some read and write threads all waiting for the _mutex, so notify_all is always helps. – yanchong May 11 '15 at 01:49
0

If using notify_all, then multiple threads can reacquire the same lock.

No. Every notified thread will compete for lock acquirision, in the same way as mutex_.lock(). Even with notify_all, at most one thread can execute critical section's code at the time. So, using notify_all is perfectly correct.

  1. how can I optimize the program? obviously, when releasing a read lock, we only need to notify the threads that attempt to acquire the write lock, since read never blocks read. So how to implement this idea?

As read never blocks read, no read thread can wait() other read thread. So even with your current code ReleaseReadLock() can only notify write threads.

Because of that, in ReleaseReadLock() you may safely use notify() instead of notify_all: no reason to awake all write threads, as only one of them can aqcuire your r/w lock.

As for other optimizations, you should better to fix artefacts, listed in this answer: https://stackoverflow.com/a/12657243/3440745

Community
  • 1
  • 1
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153