org.apache.qpid.junit.concurrency
Class TestRunnable

java.lang.Object
  extended by org.apache.qpid.junit.concurrency.TestRunnable
All Implemented Interfaces:
Runnable

public abstract class TestRunnable
extends Object
implements Runnable

TestRunnable is an extension of java.util.Runnable that adds some features to make it easier to coordinate the activities of threads in such a way as to expose bugs in multi threaded code.

Sometimes several threads will run in a particular order so that a bug is not revealed. Other times the ordering of the threads will expose a bug. Such bugs can be hard to replicate as the exact execution ordering of threads is not usually controlled. This class adds some methods that allow threads to synchronize other threads, either allowing them to run, or waiting for them to allow this thread to run. It also provides convenience methods to gather error messages and exceptions from threads, which will often be reported in unit testing code.

Coordination between threads is handled by the ThreadTestCoordinator. It is called through the convenience methods allow(int[]) and waitFor(int[], boolean). Threads to be coordinated must be set up with the coordinator and assigned integer ids. It is then possible to call the coordinator with an array of thread ids requesting that those threads be allowed to continue, or to wait until one of them allows this thread to continue. The otherwise non-deterministic execution order of threads can be controlled into a carefully determined sequence using these methods in order to reproduce race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on.

When waiting for another thread to give a signal to continue it is sometimes the case that the other thread has become blocked by the code under test. For example in testing for a dirty read (for example in database code), thread 1 lets thread 2 perform a write but not commit it, then thread 2 lets thread 1 run and attempt to perform a dirty read on its uncommitted write. Transaction synchronization code being tested against the possibility of a dirty write may make use of snapshots in which case both threads should be able to read and write without blocking. It may make use of explicit keys in which case thread 2 may become blocked on its write attempt because thread 1 holds a read lock and it must wait until thread 1 completes its transaction before it can acquire this lock. The waitFor(int[], boolean) method accepts a boolean parameter to indicate that threads being blocked (other than on the coordinator) can be interpreted the same as if the thread explicitly allows the thread calling waitFor to continue. Using this technique a dirty read test could be written that works against either the snapshot or the locking implementation, allowing both approaches to pass the test yet arranging for multiple threads to run against the implementation in such a way that a potential dirty read bug is exposed.

CRC Card
Responsibilities Collaborations
Wait for another thread to allow this one to continue.
Allow another thread to continue.
Accumulate error messages.
Record exceptions from thread run.
Maintain link to thread coordinator.
Explicitly mark a thread with an integer id.
Maintian a flag to indicate whether or not this thread is waiting on the coordinator.

Todo:
The allow then waitFor operations are very often used as a pair. So create a method allowAndWait that combines them into a single method call.

Constructor Summary
TestRunnable()
           
 
Method Summary
protected  void addErrorMessage(String message)
          Keeps the error message for later reporting by the coordinator.
protected  void allow(int[] threads)
          Produces allow events on each of the specified threads.
(package private)  String getErrorMessage()
          Reports any accumulated error messages.
(package private)  Exception getException()
          Reports any exception thrown by the runWithExceptions() method.
(package private)  Thread getThread()
          Gets the Java thread under which this runs.
(package private)  boolean isWaitingOnCoordinator()
          Reports whether or not this thread is waiting on the coordinator.
 void run()
          Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record of those exceptions.
abstract  void runWithExceptions()
          Implementations override this to perform coordinated thread sequencing.
(package private)  void setCoordinator(ThreadTestCoordinator coordinator)
          Sets the coordinator for this thread.
(package private)  void setId(int id)
          Sets up the explicit int id for this thread.
(package private)  void setThread(Thread thread)
          Sets the Java thread under which this runs.
(package private)  void setWaitingOnCoordinator(boolean waiting)
          Sets the value of the waiting on coordinator flag.
 String toString()
          Provides a string summary of this test threads status.
protected  boolean waitFor(int[] threads, boolean otherWaitIsAllow)
          Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

TestRunnable

public TestRunnable()
Method Detail

runWithExceptions

public abstract void runWithExceptions()
                                throws Exception
Implementations override this to perform coordinated thread sequencing.

Throws:
Exception - Any exception raised by the implementation will be caught by the default run() implementation for later querying by the getException() method.

run

public void run()
Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record of those exceptions. Defers to the runWithExceptions() method to provide the thread body implementation and catches any exceptions thrown by it.

Specified by:
run in interface Runnable

waitFor

protected boolean waitFor(int[] threads,
                          boolean otherWaitIsAllow)
Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs.

Parameters:
threads - The set of threads that can allow this one to continue.
otherWaitIsAllow - If set to true if the threads being waited on are blocked other than on the coordinator itself then this is to be interpreted as allowing this thread to continue.
Returns:
If the otherWaitIsAllow flag is set, then true is returned when the thread being waited on is found to be blocked outside of the thread test coordinator. false under all other conditions.

allow

protected void allow(int[] threads)
Produces allow events on each of the specified threads.

Parameters:
threads - The set of threads that are to be allowed to continue.

addErrorMessage

protected void addErrorMessage(String message)
Keeps the error message for later reporting by the coordinator.

Parameters:
message - The error message to keep.

setCoordinator

void setCoordinator(ThreadTestCoordinator coordinator)
Sets the coordinator for this thread.

Parameters:
coordinator - The coordinator for this thread.

isWaitingOnCoordinator

boolean isWaitingOnCoordinator()
Reports whether or not this thread is waiting on the coordinator.

Returns:
If this thread is waiting on the coordinator.

setWaitingOnCoordinator

void setWaitingOnCoordinator(boolean waiting)
Sets the value of the waiting on coordinator flag.

Parameters:
waiting - The value of the waiting on coordinator flag.

setId

void setId(int id)
Sets up the explicit int id for this thread.

Parameters:
id - The integer id.

getErrorMessage

String getErrorMessage()
Reports any accumulated error messages.

Returns:
Any accumulated error messages.

getException

Exception getException()
Reports any exception thrown by the runWithExceptions() method.

Returns:
Any exception thrown by the runWithExceptions() method.

setThread

void setThread(Thread thread)
Sets the Java thread under which this runs.

Parameters:
thread - The Java thread under which this runs.

getThread

Thread getThread()
Gets the Java thread under which this runs.

Returns:
The Java thread under which this runs.

toString

public String toString()
Provides a string summary of this test threads status.

Overrides:
toString in class Object
Returns:
Summarizes this threads status.


Licensed to the Apache Software Foundation