Implementing Read-Write Locks with Double-Checked Locking

929 Views Asked by At

I've written a Java ReadWriteLock where the readers use double-checked locking to acquire the write-lock. Is this unsafe (as is the case for DCL with lazy-instantiation)?

import java.util.concurrent.atomic.AtomicInteger;

public class DCLRWLock {
    private boolean readerAcquiringWriteLock = false;
    private boolean writerLock = false;
    private AtomicInteger numReaders = new AtomicInteger();

    public void readerAcquire() throws InterruptedException {
        while (!nzAndIncrement(numReaders)) {
            synchronized (this) {
                if (numReaders.get() != 0)
                    continue;
                if (readerAcquiringWriteLock) {
                    do {
                        wait();
                    } while (readerAcquiringWriteLock);
                } else {
                    readerAcquiringWriteLock = true;
                    writerAcquire();
                    readerAcquiringWriteLock = false;
                    assert numReaders.get() == 0;
                    numReaders.set(1);
                    notifyAll();
                    break;
                }
            }
        }
    }

    public void readerRelease() {
        if (numReaders.decrementAndGet() == 0)
            writerRelease();
    }

    public synchronized void writerAcquire() throws InterruptedException {
        while (writerLock)
            wait();
        writerLock = true;
    }

    public synchronized void writerRelease() {
        writerLock = false;
        notifyAll();
    }

    // Atomically:
    // If x is nonzero, increments x and returns true
    // Otherwise returns false
    private static boolean nzAndIncrement(AtomicInteger x) {
        for (;;) {
            int val = x.get();
            if (val == 0)
                return false;
            else if (x.compareAndSet(val, val + 1))
                return true;
        }
    }
}

I know that Java already has a ReentrantReadWriteLock. I'm more interested in the general question of how to determine what forms of DCL are or aren't safe?

1

There are 1 best solutions below

6
On

The unsafety of the DCL comes about when we assume that just because we read a non-null reference from a shared variable, all the writes by the thread which wrote the reference will be visible. In other words, we read a reference published via a datarace and assume things will work out fine.

In your case you don't even have a data race, but just a race condition on an atomic variable. Therefore the non-safety described above certainly does not apply here.