Pure Danger Tech


navigation
home

Writing while reads pile up

08 Mar 2009

I had a fun time speaking at the No Fluff Just Stuff Gateway Software Symposium on Friday. The home town show is always fun as there are so many friends in the crowd. Wish I could have been there Saturday and Sunday instead of in airports and planes but family business intervened.

I did have a question in one of my concurrency talks that I wanted to follow up on. The question was in the context of ReentrantReadWriteLock and whether you could use some of the extra methods in it (like getQueueLength()) to have a writing thread continue processing until some number of reader threads have piled up in need of a read. Or at least that’s how I remember the question. :)

I actually hacked something like this together just for fun in the airport yesterday. Basically I’m using a RRWL to protect access to some shared data and I have both readers and writers. When a writer is writing it actually uses the current queue length of the RRWL to decide when it should give it up:

[source:java]

lock.writeLock().lock();

try {

while(lock.getQueueLength() < MAX_WAITING_THREADS) { // process a work item } } finally { lock.writeLock().unlock(); } [/source] This does work and is possibly interesting but it’s not something I’d recommend. First, getQueueLength() is a combination of readers *and* writers waiting for the lock and there’s no way to tell which. Maybe that doesn’t matter though - maybe any kind of waiter accumulating is a good reason to release the lock. The javadoc however states that this method:

Returns an estimate of the number of threads waiting to acquire either the read or write lock. The value is only an estimate because the number of threads may change dynamically while this method traverses internal data structures. This method is designed for use in monitoring of the system state, not for synchronization control. </p> So the value not be accurate and is not designed for synchronization control. A wise man should probably heed this advice. Its repeated in a different form in the class docs as well.

I then hacked together a different solution that doesn’t rely on the lock itself but rather uses a separate resettable count of the number of waiting readers (I made it specific to readers in this case): [source:java]

class ResettableLatch {

private final int count;

private AtomicInteger waiting = new AtomicInteger(0);

public ResettableLatch(int count) {

this.count = count;

}

public void readerWaiting() {

waiting.incrementAndGet();

}

public int waitingCount() {

return waiting.get();

}

public boolean shouldRelease() {

return waiting.get() >= count;

}

public void reset() {

waiting.set(0);

}

}

[/source]

To use this latch I made readers indicate they were waiting before they tried to acquire the lock: [source:java]

latch.readerWaiting();

lock.readLock().lock();

[/source]

And then I wrote a processing loop that watched the latch in the writer: [source:java]

lock.writeLock().lock();

try {

latch.reset();

while(! latch.shouldRelease()) {

// process a work item

}

} finally {

lock.writeLock().unlock();

}

[/source]

The latch state is being updated by readers regardless of anyone is waiting but when the writer enters it resets the state. There is a small race there where readers that start reading between the write lock acquire and the latch reset and lost. That means I may lose waiters which in some circumstances could be an issue. Then I process in a loop until the latch says I’ve got too many readers piled up and waiting.

I can think of a couple other ways to potentially do this. I’d be interested in hearing other people’s thoughts as well.