import junit.framework.*; /** Attempts to test the correctness of the ReaderWriterLock class, which allows multiple reader and writer threads to * safely access a shared resource. (Multiple readers can be active at a time, but only one writer can be active, * during which time no readers can be active.) This can be difficult to test because there is little control over * how the threads are actually scheduled. * @version $Id: ReaderWriterLockTest.java 4447 2008-04-18 16:06:34Z rcartwright $ */ public class ReaderWriterLockTest extends MultiThreadedTestCase { protected ReaderWriterLock _lock; /** Creates a new lock for the tests. */ public void setUp() throws Exception { super.setUp(); _lock = new ReaderWriterLock(); } /** Number of notifications expected before we actually notify. */ private int _notifyCount = 0; /** Object to provide semaphore-like synchronization. */ private final Object _notifyObject = new Object(); /** Notifies the _notifyObject (semaphore) when the _notifyCount reaches 0. (Decrements the count on each call.) */ private void _notify() { synchronized(_notifyObject) { _notifyCount--; if (_notifyCount <= 0) { _notifyObject.notify(); _notifyCount = 0; } } } /** Tests that multiple readers can run without causing deadlock. We can't really impose any ordering on their output. */ public void testMultipleReaders() throws InterruptedException { final StringBuilder buf = new StringBuilder(); // Create three threads ReaderThread r1 = new PrinterReaderThread("r1 ", buf); ReaderThread r2 = new PrinterReaderThread("r2 ", buf); ReaderThread r3 = new PrinterReaderThread("r3 ", buf); // Init the count _notifyCount = 3; // Start the readers synchronized(_notifyObject) { r1.start(); r2.start(); r3.start(); _notifyObject.wait(); } r1.join(); r2.join(); r3.join(); } /** Tests that multiple writers run in mutually exclusive intervals without causing deadlock. */ public void testMultipleWriters() throws InterruptedException { final StringBuilder buf = new StringBuilder(); // Create three threads WriterThread w1 = new PrinterWriterThread("w1 ", buf); WriterThread w2 = new PrinterWriterThread("w2 ", buf); WriterThread w3 = new PrinterWriterThread("w3 ", buf); // Init the count _notifyCount = 3; // Start the readers synchronized(_notifyObject) { w1.start(); w2.start(); w3.start(); _notifyObject.wait(); } String output = buf.toString(); //System.out.println(output); w1.join(); w2.join(); w3.join(); // Writer output should never be interspersed. assertTrue("w1 writes should happen in order", output.indexOf("w1 w1 w1 ") != -1); assertTrue("w2 writes should happen in order", output.indexOf("w2 w2 w2 ") != -1); assertTrue("w1 writes should happen in order", output.indexOf("w3 w3 w3 ") != -1); } /** Ensure that a single thread can perform multiple reads. */ public void testReaderMultipleReads() throws InterruptedException { // Simulate a reader that performs multiple reads in one thread _lock.startRead(); _lock.startRead(); _lock.endRead(); _lock.endRead(); // Test that a reading thread can perform an additional read even if // a writing thread is waiting. _lock.startRead(); Thread w = new Thread() { public void run() { synchronized(_lock) { _lock.notifyAll(); _lock.startWrite(); // Waits here and releases _lock... _lock.endWrite(); } } }; synchronized(_lock) { w.start(); _lock.wait(); } _lock.startRead(); _lock.endRead(); _lock.endRead(); w.join(); } /** Ensure that a reading thread cannot perform a write. */ public void testCannotWriteInARead() { try { _lock.startRead(); _lock.startWrite(); fail("Should have caused an IllegalStateException!"); } catch (IllegalStateException ise) { // Good, that's what we want } } /** Ensure that a writing thread cannot perform an additional write. */ public void testCannotWriteInAWrite() { try { _lock.startWrite(); _lock.startWrite(); fail("Should have caused an IllegalStateException!"); } catch (IllegalStateException ise) { // Good, that's what we want } } /** Ensure that a writing thread cannot perform a read. */ public void testCannotReadInAWrite() { try { _lock.startWrite(); _lock.startRead(); fail("Should have caused an IllegalStateException!"); } catch (IllegalStateException ise) { // Good, that's what we want } } /** We would like to test the following schedule. * *
* W1 |***********|
* W2 |..........*****|
* R1 |..............********|
* R2 |............****|
* W3 |...................***|
* R3 |.....................****|
* R4 |................*******|
* R5 |***|
*
*
* Key: "." means waiting, "*" means running
*
* This is next to impossible to set up in Java. What we'd really
* like is a unit-testing framework that allows us to easily specify
* such a schedule in a test. (Conveniently, Corky Cartwright has
* applied for a Texas ATP grant to develop just such a framework.)
*
*
* So, instead, we'll just set up these threads, let them run, and
* enforce that no one interferes with output from a writer.
*/
public void testMultipleReadersAndWriters() throws InterruptedException {
final StringBuilder buf = new StringBuilder();
// Create threads
WriterThread w1 = new PrinterWriterThread("w1 ", buf);
WriterThread w2 = new PrinterWriterThread("w2 ", buf);
WriterThread w3 = new PrinterWriterThread("w3 ", buf);
ReaderThread r1 = new PrinterReaderThread("r1 ", buf);
ReaderThread r2 = new PrinterReaderThread("r2 ", buf);
ReaderThread r3 = new PrinterReaderThread("r3 ", buf);
ReaderThread r4 = new PrinterReaderThread("r4 ", buf);
ReaderThread r5 = new PrinterReaderThread("r5 ", buf);
// Init the count
_notifyCount = 8;
// Start the readers
synchronized(_notifyObject) {
w1.start();
w2.start();
r1.start();
r2.start();
w3.start();
r3.start();
r4.start();
r5.start();
_notifyObject.wait();
}
String output = buf.toString();
//System.out.println(output);
w1.join();
w2.join();
w3.join();
r1.join();
r2.join();
r3.join();
r4.join();
r5.join();
// Writer output should never be interspersed.
assertTrue("w1 writes should happen in order", output.indexOf("w1 w1 w1 ") != -1);
assertTrue("w2 writes should happen in order", output.indexOf("w2 w2 w2 ") != -1);
assertTrue("w1 writes should happen in order", output.indexOf("w3 w3 w3 ") != -1);
}
/** Test that even a steady stream of readers can not starve writers.
*/
public void testReaderFlood() throws InterruptedException {
final StringBuilder buf = new StringBuilder();
// Create threads
java.util.ArrayList