2
\$\begingroup\$

I'm practicing to write c++11/14 and I implemented a RW lock. Does everything look ok?

class RWLock {
public:
void readLock() {
    std::unique_lock<std::mutex> lock(mutex);
    waitingReaders.wait(lock, [this]{!writer && queuedWriters == 0});
    ++readers;
}

void readUnlock() {
    std::unique_lock<std::mutex> lock(mutex);
    --readers;
    if (readers == 0 && queuedWriters > 0) {
        lock.unlock();
        waitingWriters.notify_one();
    }
}

void writeLock() {
    std::unique_lock<std::mutex> lock(mutex);
    ++queuedWriters;
    waitingWriters.wait(lock, [this]{!writer && readers == 0});
    --queuedWriters;
    writer = true;
}

void writeUnlock() {
    std::unique_lock<std::mutex> lock(mutex);

    if(queuedWriters > 0) {
        lock.unlock();
        waitingWriters.notify_one();
    } else {
        writer = false;
        lock.unlock();
        waitingReaders.notify_all();
    }
}

private:
std::mutex mutex;
std::condition_variable waitingReaders;
std::condition_variable waitingWriters;

int readers = 0;
int queuedWriters = 0;
bool writer = false;    
};

In particular: Do I really need to take a lock in my WriteUnlock() method? It looks like all I'm doing is unlock right away.

And is it recommended to perform a lock.unlock() before I do a notify_one or a notify_all ?

Does performing a condition_variable.wait(lock, lambda) where the lambda is true cause a performance loss? Like potentially in the readLock() method

\$\endgroup\$
2
\$\begingroup\$

Do I really need to take a lock in my WriteUnlock() method? It looks like all I'm doing is unlock right away.

YES, you need the lock, because you are accessing queuedWriters and you need to make sure nobody changes it (by calling writeLock) before you notify readers or writers based on the check. You could have seen queuedWriters being zero, therefore notifying readers, but some writer could get sleeping in the meantime and never get woken.

And is it recommended to perform a lock.unlock() before I do a notify_one or a notify_all?

Unlock before, because the thread being woken would immediatelly try to hold the lock, thus being blocked again (to be woken for second time when you unlock).

Does performing a condition_variable.wait(lock, lambda) where the lambda is true cause a performance loss?

It is same as while (!pred()) wait(lck); What else could you do? It checks first, so, would not wait if the condition is true.

\$\endgroup\$
  • \$\begingroup\$ I have also always wondered if you need to unlock() before a notify*(). But I have never found a reference with this recommendation (for or against). If you have a reference for this I would love to read it. Currently I am not convinced by your logic; just because you notify the conditional does not mean that anything happens immediately. Note: I am not saying it is bad advice (it seems logical and does not seem to break things) but I would love to read from an authoritative reference. \$\endgroup\$ – Martin York May 24 '18 at 21:16
  • \$\begingroup\$ @MartinYork stackoverflow.com/questions/17101922/… \$\endgroup\$ – user52292 May 24 '18 at 22:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.