org.apache.qpid.junit.concurrency
Class ThreadTestCoordinator

java.lang.Object
  extended by org.apache.qpid.junit.concurrency.ThreadTestCoordinator

public class ThreadTestCoordinator
extends Object

ThreadTestCoordinator provides an array of binary latches that allows threads to wait for other threads or to send them a signal that allows them to continue running or to wait for another thread to signal them. The binary latch array is always a square array, allowing one latch from and to every thread. Upon accepting an allow signal from one sender the latches for all senders for a are cleared. This class is always used in conjunction with TestRunnable for writing concurrent test code that coordinates multi-threaded activity in order to reproduce concurrency bugs.

CRC Card
Responsibilities Collaborations
Accept test threads to coordinate.
Allow test threads to send 'allow to continue' signals.
Allow test threads to wait on this coordinator for 'allow to continue' signals.
Report error messages from test threads.
Report exceptions from test threads.
Provide method to wait until all test threads have completed.

Todo:
This code was hacked together as a bit of an experiment, because I wasn't sure if this idea would work. It has proved extremely usefull. Some documentation for this needs to be written to explain it better., Consider how deadlock detection will be handled. If all threads are blocking on the coordinator, waiting for each other, they are deadlocked and there is something wrong with the test code that put them in that situation. If they are all blocked elsewhere, they may be deadlocked, or could just be waiting on some external event. A timeout should be used. Timeout is already implemented, just need to sanity check how this is working and document it., Consider how livelock detection could be implemented? LockFree data structures might cause live locks. I guess a longish timeout is the only thing that can be done for that., Only course grained synchronous at the method class level can be obtained. This is because test code can only insert synchronization points between method calls it makes. So this code will not be usefull for checking sequences of events within methods, unless the code under test is explicitly instrumented for it. It might be possible to instrument code by using labels, and then use the debugger/profiler interface to put breakpoints on the labels and use them as synchronization points. Not perfect, but at the unused labels can be left in the code, without altering its behaviour.

Constructor Summary
ThreadTestCoordinator(int numThreads)
          Creates a new test thread coordinator.
ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory)
          Creates a new test thread coordinator with a specific thread factory.
 
Method Summary
 void addTestThread(TestRunnable runnable, int id)
          Adds a thread to this coordinator and assigns an id to it.
(package private)  boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller)
          Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event.
 Collection<Exception> getExceptions()
          Reports any accumulated exceptions from the test threads run methods.
 String joinAndRetrieveMessages()
          Waits until all the test threads have completed and returns any accumulated error messages from them.
(package private)  void produceAllowEvents(int[] threads, int callerId, TestRunnable caller)
          Creates a set of 'allow to continue' events on the event queues of the specified threads.
 void run()
          Starts all the coordinated threads running.
 void setDeadlockTimeout(long millis)
          Sets a timeout to break out of potential deadlocks.
 String toString()
          Pretty prints the state of the thread test coordinator, for debugging purposes.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

ThreadTestCoordinator

public ThreadTestCoordinator(int numThreads)
Creates a new test thread coordinator. The number of threads to run must be specified here.

Parameters:
numThreads - The number of threads to run.

ThreadTestCoordinator

public ThreadTestCoordinator(int numThreads,
                             ThreadFactory threadFactory)
Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be specified here.

Parameters:
numThreads - The number of threads to run.
threadFactory - The factory to use to create the test threads.
Method Detail

addTestThread

public void addTestThread(TestRunnable runnable,
                          int id)
Adds a thread to this coordinator and assigns an id to it. The ids must be numbered sequentially from 0 and it is up to the caller to do this.

Parameters:
runnable - The test thread.
id - The explicit id to assign to the test thread.

run

public void run()
Starts all the coordinated threads running.


joinAndRetrieveMessages

public String joinAndRetrieveMessages()
Waits until all the test threads have completed and returns any accumulated error messages from them. Any exceptions thrown by their run methods are also kept at this point.

Returns:
The accumulated error messages from all the threads concatenated together.

getExceptions

public Collection<Exception> getExceptions()
Reports any accumulated exceptions from the test threads run methods. This method must be called after joinAndRetrieveMessages().

Returns:
Any accumulated exceptions from the test threads run methods. This method must be called after

setDeadlockTimeout

public void setDeadlockTimeout(long millis)
Sets a timeout to break out of potential deadlocks. If all threads are waiting for other threads to send them continue events for longer than this timeout then the threads are all terminated.

Parameters:
millis - The minimum time to allow to pass before breaking out of any potential deadlocks.
Todo:
This has not been implemented yet. If a potential deadlock happens then the joinAndRetrieveMessages method should throw a PotentialDeadlockException.

produceAllowEvents

void produceAllowEvents(int[] threads,
                        int callerId,
                        TestRunnable caller)
Creates a set of 'allow to continue' events on the event queues of the specified threads.

Parameters:
threads - The set of threads to allow to continue.
callerId - The explicit id of the calling test thread.
caller - The calling test thread.

consumeAllowEvent

boolean consumeAllowEvent(int[] threads,
                          boolean otherWaitIsAllow,
                          int callerId,
                          TestRunnable caller)
Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event.

Parameters:
threads - The set of threads to accept an allow to continue event from.
otherWaitIsAllow - Whether or not to accept threads being blocked elsewhere as permission to continue.
callerId - The explicit id of the calling test thread.
caller - The calling test thread.
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.

toString

public String toString()
Pretty prints the state of the thread test coordinator, for debugging purposes.

Overrides:
toString in class Object
Returns:
Pretty printed state of the thread test coordinator.


Licensed to the Apache Software Foundation