I found myself in need of a lock that allows my server to barge the write lock that might be acquired by my users. It would never interrupt them, but it would jump the line so the next time the write lock is available, my server gets it instead. It's quite simple, but I'm wondering if there's something I might have missed. I'm obviously open to feedback on all aspects, because why not, but I'm primarily interested in feedback on correctness and bugs.
/**
* An implementation of a lock with 3 locking levels.
*
* The first level is READ - any number of readers may read concurrently as per
* the specification of {@link ReentrantReadWriteLock}.
*
* The second level is WRITE - only one writer may write at a time, as per the
* specification of {@link ReentrantReadWriteLock}.
*
* The final level is BARGE - a barge request will skip the line to perform an
* operation with write level access with priority over the others. Competing
* barge requests will resolve themselves as per the Write specification of
* {@link ReentrantReadWriteLock} (but they only compete with other Barge
* requests, not Write requests.)
*
* The intended use case is for mission critical work. Consider users making
* requests with read and write privileges and a server that needs periodic
* maintenance routines. The server should not wait in line behind users.
* Certain admin requests may also need to barge to preempt a malicious actor
* from flooding the server. If the admin request had to wait in line behind
* all the malicious requests, untold damage could be done in the meanwhile.
*
*/
public class BargeLock {
/**
* The typical read-write lock. This implentation should, in time, proxy
* many of the methods to this.
*/
private final ReentrantReadWriteLock standard = new ReentrantReadWriteLock(true);
/**
* The barge lock - readers and writers of the standard lock will READ from
* this lock so they all read concurrently. Bargers will write to this, so
* all other users wait behind the barge.
*/
private final ReentrantReadWriteLock barge = new ReentrantReadWriteLock(true);
/**
* Perform some operation within the safety of a read block, concurrently
* with other read operations, and blocked by all write and barge operations.
* @param lambda The operation to perform. Users are responsible for handling
* input and output outside of this method
*/
public void performInsideRead(Runnable lambda) {
try {
barge.readLock().lock();
try {
standard.readLock().lock();
lambda.run();
} finally {
standard.readLock().unlock();
}
} finally {
barge.readLock().unlock();
}
}
/**
* Perform some operation within the safety of a write block, blocking all
* read operations and potentially being blocked by earlier arriving write
* operations, and potentially being blocked by barge operations.
* @param lambda The operation to perform. Users are responsible for
* handling input and output outside of this method
*/
public void performInsideWrite(Runnable lambda) {
try {
barge.readLock().lock();
try {
standard.writeLock().lock();
lambda.run();
} finally {
standard.writeLock().unlock();
}
} finally {
barge.readLock().unlock();
}
}
/**
* Perform an operation with priority over all writes and reads. Blocks
* only from earlier arriving barges.
* @param lambda The operation to perform. Users are responsible for
* handling input and output outside of this method.
*/
public void performInsideBarge(Runnable lambda) {
try {
barge.writeLock().lock();
try {
standard.writeLock().lock();
lambda.run();
} finally {
standard.writeLock().unlock();
}
} finally {
barge.writeLock().unlock();
}
}
}