Concurrency Unit Test Waynes Attempt

From ExtremeProgrammingChallengeFourteenTheBug


This test exposes the error with the message "producer1 blocked". It's verbose and it's slow (takes seconds to execute), and I hate that it's not deterministic. It can also report false errors if the process doesn't get any CPU time for 200 msec.

  public void testTwoProducersTwoConsumers() throws Exception {
final int numberOfItems = 10000;
final Watchdog watchdog = new Watchdog();
final BoundedBuffer buffer = new BoundedBuffer(1);
Thread producer1 = new Producer(buffer, watchdog, numberOfItems);
Thread producer2 = new Producer(buffer, watchdog, numberOfItems);
Thread consumer1 = new Consumer(buffer, watchdog, numberOfItems);
Thread consumer2 = new Consumer(buffer, watchdog, numberOfItems);
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
watchdog.waitUntilInactive();
assert("producer1 blocked", !producer1.isAlive());
assert("producer2 blocked", !producer2.isAlive());
assert("consumer1 blocked", !consumer1.isAlive());
assert("consumer2 blocked", !consumer2.isAlive());
  }
Watchdog is how I detect that nothing is happening. It allows the test to work on faster or slower CPUs:

private static class Watchdog {
  private long msecOfLastReset;
  public Watchdog() {
reset();
  }
  public synchronized void reset() {
msecOfLastReset = System.currentTimeMillis();
  }
  public void waitUntilInactive() throws InterruptedException {
while (msecSinceReset() < 200)
Thread.sleep(200);
  }
  private synchronized long msecSinceReset() {
return System.currentTimeMillis() - msecOfLastReset;
  }
};
The producers and consumers are about what you'd expect (I expect):

private abstract static class Actor extends Thread {
  protected BoundedBuffer buffer;
  private Watchdog watchdog;
  private int numberOfItems;
  public Actor(BoundedBuffer buffer, Watchdog watchdog, int numberOfItems) {
this.buffer = buffer;
this.watchdog = watchdog;
this.numberOfItems = numberOfItems;
  }
  public void run() {
try {
for (int i = 0; i < numberOfItems; i++) {
act();
watchdog.reset();
}
}
catch (InterruptedException e) {
}
  }
  protected abstract void act() throws InterruptedException;
};

private static class Producer extends Actor { public Producer(BoundedBuffer buffer, Watchdog watchdog, int numberOfItems) { super(buffer, watchdog, numberOfItems); } protected void act() throws InterruptedException { buffer.put(new Object()); } };

private static class Consumer extends Actor { public Consumer(BoundedBuffer buffer, Watchdog watchdog, int numberOfItems) { super(buffer, watchdog, numberOfItems); } protected void act() throws InterruptedException { buffer.take(); } };
I added a new constructor to BoundedBuffer that takes the buffer size, as I wasn't able to reproduce the deadlock with the default buffer size of 4.

Also, my test for one producer and two consumers showed a deadlock, as did my test for two producers and one consumer. One producer and one consumer worked fine. When I changed the notify() to notifyAll(), all the tests passed. -- WayneConrad


EditText of this page (last edited March 6, 2012) or FindPage with title or text search