You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2008/04/24 19:51:14 UTC

svn commit: r651325 [19/37] - in /incubator/qpid/trunk/qpid/java: broker/src/main/java/org/apache/qpid/server/exchange/ broker/src/main/java/org/apache/qpid/server/handler/ broker/src/main/java/org/apache/qpid/server/output/ broker/src/main/java/org/ap...

Modified: incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java?rev=651325&r1=651324&r2=651325&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java (original)
+++ incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java Thu Apr 24 10:49:03 2008
@@ -1,485 +1,485 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.junit.concurrency;
-
-import org.apache.log4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * 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
- * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce
- * concurrency bugs.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Accept test threads to coordinate.
- * <tr><td> Allow test threads to send 'allow to continue' signals.
- * <tr><td> Allow test threads to wait on this coordinator for 'allow to continue' signals.
- * <tr><td> Report error messages from test threads.
- * <tr><td> Report exceptions from test threads.
- * <tr><td> Provide method to wait until all test threads have completed.
- * </table>
- *
- * @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.
- *
- * @todo 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.
- *
- * @todo 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.
- *
- * @todo 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.
- *
- * @author Rupert Smith
- */
-public class ThreadTestCoordinator
-{
-    /** Used for logging. */
-    private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class);
-
-    /** Keeps track of the test threads by their ids. */
-    private TestRunnable[] testThreads; // = new TestRunnable[2];
-
-    /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */
-    private final Object coordinatorLock = new Object();
-
-    /** A set of monitors for each test thread. */
-    private Object[] locks;
-
-    /** The binary latch array, this is always a square array allowing one event from and to every thread. */
-    private boolean[][] allowEvents;
-
-    /** Keeps track of the number of threads being coordinated. */
-    private int threadCount = 0;
-
-    /** Accumulates any exceptions resulting from the threads run methods. */
-    private Collection<Exception> exceptions = new ArrayList<Exception>();
-
-    /**
-     * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential
-     * deadlock may be happening.
-     */
-    private long deadlockTimeout = 1000 * 1000000;
-
-    /** Holds the factory to create test thread with. */
-    private ThreadFactory threadFactory;
-
-    /**
-     * Creates a new test thread coordinator. The number of threads to run must be specified here.
-     *
-     * @param numThreads The number of threads to run.
-     */
-    public ThreadTestCoordinator(int numThreads)
-    {
-        this.threadCount = numThreads;
-
-        // Create an array big enough to hold all the test threads.
-        testThreads = new TestRunnable[threadCount];
-
-        // Use the default thread factory, as none specified.
-        threadFactory = new DefaultThreadFactory();
-    }
-
-    /**
-     * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be
-     * specified here.
-     *
-     * @param numThreads    The number of threads to run.
-     * @param threadFactory The factory to use to create the test threads.
-     */
-    public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory)
-    {
-        this.threadCount = numThreads;
-
-        // Create an array big enough to hold all the test threads.
-        testThreads = new TestRunnable[threadCount];
-
-        // Use the specified thread factory.
-        this.threadFactory = threadFactory;
-    }
-
-    /**
-     * 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.
-     *
-     * @param runnable The test thread.
-     * @param id       The explicit id to assign to the test thread.
-     */
-    public void addTestThread(TestRunnable runnable, int id)
-    {
-        testThreads[id] = runnable;
-        runnable.setCoordinator(this);
-        runnable.setId(id);
-    }
-
-    /**
-     * Starts all the coordinated threads running.
-     */
-    public void run()
-    {
-        // Create the monitors for each thread.
-        locks = new Object[threadCount];
-
-        // Create an appropriately sized event queue to allow one event from and to each thread.
-        allowEvents = new boolean[threadCount][threadCount];
-
-        // Initialize the monitors and clear the event queues.
-        for (int i = 0; i < locks.length; i++)
-        {
-            locks[i] = new Object();
-
-            for (int j = 0; j < locks.length; j++)
-            {
-                allowEvents[i][j] = false;
-            }
-        }
-
-        // Start all the threads running.
-        for (TestRunnable nextRunnable : testThreads)
-        {
-            // Create a Java thread for the test thread.
-            Thread newThread = threadFactory.newThread(nextRunnable);
-            nextRunnable.setThread(newThread);
-
-            // Start it running.
-            newThread.start();
-        }
-    }
-
-    /**
-     * 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.
-     *
-     * @return The accumulated error messages from all the threads concatenated together.
-     */
-    public String joinAndRetrieveMessages()
-    {
-        // Create an empty error message.
-        String errorMessage = "";
-
-        // Join all the test threads.
-        for (TestRunnable r : testThreads)
-        {
-            Thread t = r.getThread();
-
-            try
-            {
-                t.join();
-            }
-            catch (InterruptedException e)
-            { }
-
-            // Add any accumulated error messages to the return value.
-            errorMessage += r.getErrorMessage();
-
-            // Keep any exceptions resulting from the threads run method.
-            Exception e = r.getException();
-
-            if (e != null)
-            {
-                exceptions.add(e);
-            }
-        }
-
-        return errorMessage;
-    }
-
-    /**
-     * Reports any accumulated exceptions from the test threads run methods. This method must be called after
-     * {@link #joinAndRetrieveMessages}.
-     *
-     * @return Any accumulated exceptions from the test threads run methods. This method must be called after
-     */
-    public Collection<Exception> getExceptions()
-    {
-        return exceptions;
-    }
-
-    /**
-     * 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.
-     *
-     * @param 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.
-     */
-    public void setDeadlockTimeout(long millis)
-    {
-        deadlockTimeout = millis * 1000000;
-    }
-
-    /**
-     * Creates a set of 'allow to continue' events on the event queues of the specified threads.
-     *
-     * @param threads  The set of threads to allow to continue.
-     * @param callerId The explicit id of the calling test thread.
-     * @param caller   The calling test thread.
-     */
-    void produceAllowEvents(int[] threads, int callerId, TestRunnable caller)
-    {
-        // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
-        String message = "Thread " + callerId + " is allowing threads [ ";
-
-        for (int j = 0; j < threads.length; j++)
-        {
-            message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
-        }
-
-        message += " ] to continue.";
-        log.debug(message);
-
-        // For each allow event, synchronize on the threads lock then set the event flag to true.
-        for (int id : threads)
-        {
-            // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
-            // being blocked at this time.
-            caller.setWaitingOnCoordinator(true);
-
-            synchronized (locks[id])
-            {
-                // Release the wating on coordinator flag now that this thread is running again.
-                caller.setWaitingOnCoordinator(false);
-
-                // Send the allow to continue event to the receiving thread.
-                allowEvents[id][callerId] = true;
-            }
-        }
-
-        // Wake up any threads waiting on the coordinator lock to recheck their event queues.
-        // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
-        // being blocked at this time.
-        caller.setWaitingOnCoordinator(true);
-
-        synchronized (coordinatorLock)
-        {
-            // Release the wating on coordinator flag now that this thread is running again.
-            caller.setWaitingOnCoordinator(false);
-            coordinatorLock.notifyAll();
-        }
-    }
-
-    /**
-     * 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.
-     *
-     * @param threads          The set of threads to accept an allow to continue event from.
-     * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue.
-     * @param callerId         The explicit id of the calling test thread.
-     * @param caller           The calling test thread.
-     *
-     * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found
-     *         to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions.
-     */
-    boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller)
-    {
-        // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
-        String message = "Thread " + callerId + " is requesting threads [ ";
-
-        // Record the time at which this method was called. Will be used for breaking out of potential deadlocks.
-        long startTime = System.nanoTime();
-
-        for (int j = 0; j < threads.length; j++)
-        {
-            message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
-        }
-
-        message += " ] to allow it to continue.";
-        log.debug(message);
-
-        // Loop until an allow to continue event is received.
-        while (true)
-        {
-            // Look at all the allowing thread to see if one has created an event for consumption.
-            for (int allowerId : threads)
-            {
-                // Get the threads lock for the event to consume.
-                // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
-                // being blocked at this time.
-                caller.setWaitingOnCoordinator(true);
-
-                synchronized (locks[callerId])
-                {
-                    // Release the wating on coordinator flag now that this thread is running again.
-                    caller.setWaitingOnCoordinator(false);
-
-                    // Check if there is an event on the queue from the allowing thread to this one.
-                    if (allowEvents[callerId][allowerId])
-                    {
-                        log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId
-                            + ", to continue.");
-
-                        // Consume all the allow events for this thread.
-                        /*for (int i = 0; i < allowEvents[callerId].length; i++)
-                        {
-                            allowEvents[callerId][i] = false;
-                        }*/
-
-                        // Consume just the event from the allower to the consumer, leaving other pending allow events alone.
-                        allowEvents[callerId][allowerId] = false;
-
-                        return false;
-                    }
-                }
-            }
-
-            // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status
-            // for the threads being waited on to see if any are blocked on other resources.
-            if (otherWaitIsAllow)
-            {
-                log.debug("Other wait is to be interpreted as an allow event.");
-
-                // Look at all the potential allower threads.
-                for (int allowerId : threads)
-                {
-                    // Get the Java thread state for the allowing thread.
-                    Thread threadToTest = testThreads[allowerId].getThread();
-                    Thread.State state = threadToTest.getState();
-
-                    // Check if the thread is blocked and so a potential candidate for releasing this one.
-                    if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING)
-                            || (state == Thread.State.TIMED_WAITING))
-                    {
-                        log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating.");
-
-                        // Check that the allower thread is not waiting on the coordinator lock or any of the
-                        // individual thread locks. It must be waiting or blocked on another monitor.
-                        TestRunnable allowingRunnable = testThreads[allowerId];
-                        boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator();
-
-                        if (!isWaitingOnCoordinator)
-                        {
-                            log.debug("The allower thread, id = " + allowerId
-                                + ", is blocked or waiting other than on the coordinator.");
-
-                            // Get the threads lock for the event to consume.
-                            caller.setWaitingOnCoordinator(true);
-
-                            synchronized (locks[callerId])
-                            {
-                                caller.setWaitingOnCoordinator(false);
-
-                                // Consume all the allow events for this thread.
-                                for (int i = 0; i < allowEvents[callerId].length; i++)
-                                {
-                                    allowEvents[callerId][i] = false;
-                                }
-
-                                return true;
-                            }
-                        }
-                        else
-                        {
-                            log.debug("The waiting allower thread, " + allowerId
-                                + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue.");
-                        }
-                    }
-                }
-            }
-
-            // Keep waiting until an 'allow to continue' event can be consumed.
-            try
-            {
-                // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
-                // being blocked at this time.
-                caller.setWaitingOnCoordinator(true);
-
-                synchronized (coordinatorLock)
-                {
-                    // Release the wating on coordinator flag now that this thread is running again.
-                    caller.setWaitingOnCoordinator(false);
-
-                    log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events.");
-
-                    // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
-                    // being blocked at this time.
-                    caller.setWaitingOnCoordinator(true);
-                    coordinatorLock.wait(10);
-                }
-            }
-            catch (InterruptedException e)
-            { }
-
-            // Release the waiting on coordinator flag now that this thread is running again.
-            caller.setWaitingOnCoordinator(false);
-
-            // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible
-            // deadlock exception if so.
-            long waitTime = System.nanoTime() - startTime;
-            log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds.");
-
-            if (waitTime > deadlockTimeout)
-            {
-                // Throw a possible deadlock exception.
-                throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this);
-            }
-
-            log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available.");
-        }
-    }
-
-    /**
-     * Pretty prints the state of the thread test coordinator, for debugging purposes.
-     *
-     * @return Pretty printed state of the thread test coordinator.
-     */
-    public String toString()
-    {
-        String result = "[";
-
-        for (int i = 0; i < allowEvents.length; i++)
-        {
-            for (int j = 0; j < allowEvents[i].length; j++)
-            {
-                result += allowEvents[i][j];
-
-                result += (j < (allowEvents[i].length - 1)) ? ", " : "";
-            }
-
-            result += (i < (allowEvents.length - 1)) ? ",\n " : "";
-        }
-
-        result += "]";
-
-        for (int i = 0; i < testThreads.length; i++)
-        {
-            result += "thread[" + i + "] = " + testThreads[i].toString();
-        }
-
-        return result;
-    }
-
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.concurrency;
+
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * 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
+ * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce
+ * concurrency bugs.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept test threads to coordinate.
+ * <tr><td> Allow test threads to send 'allow to continue' signals.
+ * <tr><td> Allow test threads to wait on this coordinator for 'allow to continue' signals.
+ * <tr><td> Report error messages from test threads.
+ * <tr><td> Report exceptions from test threads.
+ * <tr><td> Provide method to wait until all test threads have completed.
+ * </table>
+ *
+ * @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.
+ *
+ * @todo 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.
+ *
+ * @todo 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.
+ *
+ * @todo 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.
+ *
+ * @author Rupert Smith
+ */
+public class ThreadTestCoordinator
+{
+    /** Used for logging. */
+    private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class);
+
+    /** Keeps track of the test threads by their ids. */
+    private TestRunnable[] testThreads; // = new TestRunnable[2];
+
+    /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */
+    private final Object coordinatorLock = new Object();
+
+    /** A set of monitors for each test thread. */
+    private Object[] locks;
+
+    /** The binary latch array, this is always a square array allowing one event from and to every thread. */
+    private boolean[][] allowEvents;
+
+    /** Keeps track of the number of threads being coordinated. */
+    private int threadCount = 0;
+
+    /** Accumulates any exceptions resulting from the threads run methods. */
+    private Collection<Exception> exceptions = new ArrayList<Exception>();
+
+    /**
+     * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential
+     * deadlock may be happening.
+     */
+    private long deadlockTimeout = 1000 * 1000000;
+
+    /** Holds the factory to create test thread with. */
+    private ThreadFactory threadFactory;
+
+    /**
+     * Creates a new test thread coordinator. The number of threads to run must be specified here.
+     *
+     * @param numThreads The number of threads to run.
+     */
+    public ThreadTestCoordinator(int numThreads)
+    {
+        this.threadCount = numThreads;
+
+        // Create an array big enough to hold all the test threads.
+        testThreads = new TestRunnable[threadCount];
+
+        // Use the default thread factory, as none specified.
+        threadFactory = new DefaultThreadFactory();
+    }
+
+    /**
+     * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be
+     * specified here.
+     *
+     * @param numThreads    The number of threads to run.
+     * @param threadFactory The factory to use to create the test threads.
+     */
+    public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory)
+    {
+        this.threadCount = numThreads;
+
+        // Create an array big enough to hold all the test threads.
+        testThreads = new TestRunnable[threadCount];
+
+        // Use the specified thread factory.
+        this.threadFactory = threadFactory;
+    }
+
+    /**
+     * 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.
+     *
+     * @param runnable The test thread.
+     * @param id       The explicit id to assign to the test thread.
+     */
+    public void addTestThread(TestRunnable runnable, int id)
+    {
+        testThreads[id] = runnable;
+        runnable.setCoordinator(this);
+        runnable.setId(id);
+    }
+
+    /**
+     * Starts all the coordinated threads running.
+     */
+    public void run()
+    {
+        // Create the monitors for each thread.
+        locks = new Object[threadCount];
+
+        // Create an appropriately sized event queue to allow one event from and to each thread.
+        allowEvents = new boolean[threadCount][threadCount];
+
+        // Initialize the monitors and clear the event queues.
+        for (int i = 0; i < locks.length; i++)
+        {
+            locks[i] = new Object();
+
+            for (int j = 0; j < locks.length; j++)
+            {
+                allowEvents[i][j] = false;
+            }
+        }
+
+        // Start all the threads running.
+        for (TestRunnable nextRunnable : testThreads)
+        {
+            // Create a Java thread for the test thread.
+            Thread newThread = threadFactory.newThread(nextRunnable);
+            nextRunnable.setThread(newThread);
+
+            // Start it running.
+            newThread.start();
+        }
+    }
+
+    /**
+     * 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.
+     *
+     * @return The accumulated error messages from all the threads concatenated together.
+     */
+    public String joinAndRetrieveMessages()
+    {
+        // Create an empty error message.
+        String errorMessage = "";
+
+        // Join all the test threads.
+        for (TestRunnable r : testThreads)
+        {
+            Thread t = r.getThread();
+
+            try
+            {
+                t.join();
+            }
+            catch (InterruptedException e)
+            { }
+
+            // Add any accumulated error messages to the return value.
+            errorMessage += r.getErrorMessage();
+
+            // Keep any exceptions resulting from the threads run method.
+            Exception e = r.getException();
+
+            if (e != null)
+            {
+                exceptions.add(e);
+            }
+        }
+
+        return errorMessage;
+    }
+
+    /**
+     * Reports any accumulated exceptions from the test threads run methods. This method must be called after
+     * {@link #joinAndRetrieveMessages}.
+     *
+     * @return Any accumulated exceptions from the test threads run methods. This method must be called after
+     */
+    public Collection<Exception> getExceptions()
+    {
+        return exceptions;
+    }
+
+    /**
+     * 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.
+     *
+     * @param 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.
+     */
+    public void setDeadlockTimeout(long millis)
+    {
+        deadlockTimeout = millis * 1000000;
+    }
+
+    /**
+     * Creates a set of 'allow to continue' events on the event queues of the specified threads.
+     *
+     * @param threads  The set of threads to allow to continue.
+     * @param callerId The explicit id of the calling test thread.
+     * @param caller   The calling test thread.
+     */
+    void produceAllowEvents(int[] threads, int callerId, TestRunnable caller)
+    {
+        // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
+        String message = "Thread " + callerId + " is allowing threads [ ";
+
+        for (int j = 0; j < threads.length; j++)
+        {
+            message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
+        }
+
+        message += " ] to continue.";
+        log.debug(message);
+
+        // For each allow event, synchronize on the threads lock then set the event flag to true.
+        for (int id : threads)
+        {
+            // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+            // being blocked at this time.
+            caller.setWaitingOnCoordinator(true);
+
+            synchronized (locks[id])
+            {
+                // Release the wating on coordinator flag now that this thread is running again.
+                caller.setWaitingOnCoordinator(false);
+
+                // Send the allow to continue event to the receiving thread.
+                allowEvents[id][callerId] = true;
+            }
+        }
+
+        // Wake up any threads waiting on the coordinator lock to recheck their event queues.
+        // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+        // being blocked at this time.
+        caller.setWaitingOnCoordinator(true);
+
+        synchronized (coordinatorLock)
+        {
+            // Release the wating on coordinator flag now that this thread is running again.
+            caller.setWaitingOnCoordinator(false);
+            coordinatorLock.notifyAll();
+        }
+    }
+
+    /**
+     * 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.
+     *
+     * @param threads          The set of threads to accept an allow to continue event from.
+     * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue.
+     * @param callerId         The explicit id of the calling test thread.
+     * @param caller           The calling test thread.
+     *
+     * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found
+     *         to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions.
+     */
+    boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller)
+    {
+        // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
+        String message = "Thread " + callerId + " is requesting threads [ ";
+
+        // Record the time at which this method was called. Will be used for breaking out of potential deadlocks.
+        long startTime = System.nanoTime();
+
+        for (int j = 0; j < threads.length; j++)
+        {
+            message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
+        }
+
+        message += " ] to allow it to continue.";
+        log.debug(message);
+
+        // Loop until an allow to continue event is received.
+        while (true)
+        {
+            // Look at all the allowing thread to see if one has created an event for consumption.
+            for (int allowerId : threads)
+            {
+                // Get the threads lock for the event to consume.
+                // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+                // being blocked at this time.
+                caller.setWaitingOnCoordinator(true);
+
+                synchronized (locks[callerId])
+                {
+                    // Release the wating on coordinator flag now that this thread is running again.
+                    caller.setWaitingOnCoordinator(false);
+
+                    // Check if there is an event on the queue from the allowing thread to this one.
+                    if (allowEvents[callerId][allowerId])
+                    {
+                        log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId
+                            + ", to continue.");
+
+                        // Consume all the allow events for this thread.
+                        /*for (int i = 0; i < allowEvents[callerId].length; i++)
+                        {
+                            allowEvents[callerId][i] = false;
+                        }*/
+
+                        // Consume just the event from the allower to the consumer, leaving other pending allow events alone.
+                        allowEvents[callerId][allowerId] = false;
+
+                        return false;
+                    }
+                }
+            }
+
+            // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status
+            // for the threads being waited on to see if any are blocked on other resources.
+            if (otherWaitIsAllow)
+            {
+                log.debug("Other wait is to be interpreted as an allow event.");
+
+                // Look at all the potential allower threads.
+                for (int allowerId : threads)
+                {
+                    // Get the Java thread state for the allowing thread.
+                    Thread threadToTest = testThreads[allowerId].getThread();
+                    Thread.State state = threadToTest.getState();
+
+                    // Check if the thread is blocked and so a potential candidate for releasing this one.
+                    if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING)
+                            || (state == Thread.State.TIMED_WAITING))
+                    {
+                        log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating.");
+
+                        // Check that the allower thread is not waiting on the coordinator lock or any of the
+                        // individual thread locks. It must be waiting or blocked on another monitor.
+                        TestRunnable allowingRunnable = testThreads[allowerId];
+                        boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator();
+
+                        if (!isWaitingOnCoordinator)
+                        {
+                            log.debug("The allower thread, id = " + allowerId
+                                + ", is blocked or waiting other than on the coordinator.");
+
+                            // Get the threads lock for the event to consume.
+                            caller.setWaitingOnCoordinator(true);
+
+                            synchronized (locks[callerId])
+                            {
+                                caller.setWaitingOnCoordinator(false);
+
+                                // Consume all the allow events for this thread.
+                                for (int i = 0; i < allowEvents[callerId].length; i++)
+                                {
+                                    allowEvents[callerId][i] = false;
+                                }
+
+                                return true;
+                            }
+                        }
+                        else
+                        {
+                            log.debug("The waiting allower thread, " + allowerId
+                                + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue.");
+                        }
+                    }
+                }
+            }
+
+            // Keep waiting until an 'allow to continue' event can be consumed.
+            try
+            {
+                // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+                // being blocked at this time.
+                caller.setWaitingOnCoordinator(true);
+
+                synchronized (coordinatorLock)
+                {
+                    // Release the wating on coordinator flag now that this thread is running again.
+                    caller.setWaitingOnCoordinator(false);
+
+                    log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events.");
+
+                    // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+                    // being blocked at this time.
+                    caller.setWaitingOnCoordinator(true);
+                    coordinatorLock.wait(10);
+                }
+            }
+            catch (InterruptedException e)
+            { }
+
+            // Release the waiting on coordinator flag now that this thread is running again.
+            caller.setWaitingOnCoordinator(false);
+
+            // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible
+            // deadlock exception if so.
+            long waitTime = System.nanoTime() - startTime;
+            log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds.");
+
+            if (waitTime > deadlockTimeout)
+            {
+                // Throw a possible deadlock exception.
+                throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this);
+            }
+
+            log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available.");
+        }
+    }
+
+    /**
+     * Pretty prints the state of the thread test coordinator, for debugging purposes.
+     *
+     * @return Pretty printed state of the thread test coordinator.
+     */
+    public String toString()
+    {
+        String result = "[";
+
+        for (int i = 0; i < allowEvents.length; i++)
+        {
+            for (int j = 0; j < allowEvents[i].length; j++)
+            {
+                result += allowEvents[i][j];
+
+                result += (j < (allowEvents[i].length - 1)) ? ", " : "";
+            }
+
+            result += (i < (allowEvents.length - 1)) ? ",\n " : "";
+        }
+
+        result += "]";
+
+        for (int i = 0; i < testThreads.length; i++)
+        {
+            result += "thread[" + i + "] = " + testThreads[i].toString();
+        }
+
+        return result;
+    }
+
+}

Propchange: incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java?rev=651325&r1=651324&r2=651325&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java (original)
+++ incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java Thu Apr 24 10:49:03 2008
@@ -1,145 +1,145 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.junit.concurrency;
-
-import org.apache.log4j.Logger;
-
-/**
- * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Demo multi-threaded testing.
- * </table>
- *
- * @author Rupert Smith
- */
-public class ThreadTestExample
-{
-    /** Used for logging. */
-    private static final Logger log = Logger.getLogger(ThreadTestExample.class);
-
-    /** Test thread 1. */
-    TestRunnable testThread1 =
-        new TestRunnable()
-        {
-            public void runWithExceptions() throws Exception
-            {
-                log.debug("public void run(): called");
-                log.info("in testThread0, block 1");
-
-                // Wait for t2 to allow t1 to continue.
-                allow(new int[] { 1 });
-                waitFor(new int[] { 1 }, false);
-
-                log.info("in testThread0, block 2");
-
-                // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit
-                // permission to allow t1 to continue.
-                allow(new int[] { 1 });
-                waitFor(new int[] { 1 }, true);
-
-                log.info("in testThread0, block 3");
-
-                // Release thread 2 from waiting on the shared lock.
-                synchronized (sharedLock)
-                {
-                    sharedLock.notifyAll();
-                }
-
-                allow(new int[] { 1 });
-            }
-        };
-
-    /** A shared lock between the test threads. */
-    final Object sharedLock = new Object();
-
-    /** Test thread 2. */
-    TestRunnable testThread2 =
-        new TestRunnable()
-        {
-            public void runWithExceptions() throws Exception
-            {
-                log.debug("public void run(): called");
-                log.info("in testThread1, block 1");
-
-                // Wait for t1 to allow t2 to continue.
-                allow(new int[] { 0 });
-                waitFor(new int[] { 0 }, false);
-
-                log.info("in testThread1, block 2");
-
-                // Wait on another resource. T1 should accept this as permission to continue.
-                try
-                {
-                    synchronized (sharedLock)
-                    {
-                        log.debug("in testThread1, waiting on shared lock.");
-                        sharedLock.wait();
-                    }
-                }
-                catch (InterruptedException e)
-                {
-                    // Bail-out with a runtime if this happens.
-                    throw new RuntimeException("Interrupted whilst waiting for shared lock.", e);
-                }
-
-                log.info("in testThread1, finished waiting on shared lock.");
-
-                // allow(new int[] { 0 });
-
-                // Wait for t1 to allow t2 to continue.
-                waitFor(new int[] { 0 }, false);
-
-                log.info("in testThread1, block 3");
-
-                allow(new int[] { 0 });
-            }
-        };
-
-    /**
-     * Executes the test threads with coordination.
-     *
-     * @param args Ignored.
-     */
-    public void main(String[] args)
-    {
-        ThreadTestCoordinator tt = new ThreadTestCoordinator(2);
-
-        tt.addTestThread(testThread1, 0);
-        tt.addTestThread(testThread2, 1);
-        tt.setDeadlockTimeout(500);
-        tt.run();
-
-        String errorMessage = tt.joinAndRetrieveMessages();
-
-        // Print any error messages or exceptions.
-        log.info(errorMessage);
-
-        if (!tt.getExceptions().isEmpty())
-        {
-            for (Exception e : tt.getExceptions())
-            {
-                log.warn("Exception thrown during test thread: ", e);
-            }
-        }
-    }
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.concurrency;
+
+import org.apache.log4j.Logger;
+
+/**
+ * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Demo multi-threaded testing.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ThreadTestExample
+{
+    /** Used for logging. */
+    private static final Logger log = Logger.getLogger(ThreadTestExample.class);
+
+    /** Test thread 1. */
+    TestRunnable testThread1 =
+        new TestRunnable()
+        {
+            public void runWithExceptions() throws Exception
+            {
+                log.debug("public void run(): called");
+                log.info("in testThread0, block 1");
+
+                // Wait for t2 to allow t1 to continue.
+                allow(new int[] { 1 });
+                waitFor(new int[] { 1 }, false);
+
+                log.info("in testThread0, block 2");
+
+                // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit
+                // permission to allow t1 to continue.
+                allow(new int[] { 1 });
+                waitFor(new int[] { 1 }, true);
+
+                log.info("in testThread0, block 3");
+
+                // Release thread 2 from waiting on the shared lock.
+                synchronized (sharedLock)
+                {
+                    sharedLock.notifyAll();
+                }
+
+                allow(new int[] { 1 });
+            }
+        };
+
+    /** A shared lock between the test threads. */
+    final Object sharedLock = new Object();
+
+    /** Test thread 2. */
+    TestRunnable testThread2 =
+        new TestRunnable()
+        {
+            public void runWithExceptions() throws Exception
+            {
+                log.debug("public void run(): called");
+                log.info("in testThread1, block 1");
+
+                // Wait for t1 to allow t2 to continue.
+                allow(new int[] { 0 });
+                waitFor(new int[] { 0 }, false);
+
+                log.info("in testThread1, block 2");
+
+                // Wait on another resource. T1 should accept this as permission to continue.
+                try
+                {
+                    synchronized (sharedLock)
+                    {
+                        log.debug("in testThread1, waiting on shared lock.");
+                        sharedLock.wait();
+                    }
+                }
+                catch (InterruptedException e)
+                {
+                    // Bail-out with a runtime if this happens.
+                    throw new RuntimeException("Interrupted whilst waiting for shared lock.", e);
+                }
+
+                log.info("in testThread1, finished waiting on shared lock.");
+
+                // allow(new int[] { 0 });
+
+                // Wait for t1 to allow t2 to continue.
+                waitFor(new int[] { 0 }, false);
+
+                log.info("in testThread1, block 3");
+
+                allow(new int[] { 0 });
+            }
+        };
+
+    /**
+     * Executes the test threads with coordination.
+     *
+     * @param args Ignored.
+     */
+    public void main(String[] args)
+    {
+        ThreadTestCoordinator tt = new ThreadTestCoordinator(2);
+
+        tt.addTestThread(testThread1, 0);
+        tt.addTestThread(testThread2, 1);
+        tt.setDeadlockTimeout(500);
+        tt.run();
+
+        String errorMessage = tt.joinAndRetrieveMessages();
+
+        // Print any error messages or exceptions.
+        log.info(errorMessage);
+
+        if (!tt.getExceptions().isEmpty())
+        {
+            for (Exception e : tt.getExceptions())
+            {
+                log.warn("Exception thrown during test thread: ", e);
+            }
+        }
+    }
+}

Propchange: incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java?rev=651325&r1=651324&r2=651325&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java (original)
+++ incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java Thu Apr 24 10:49:03 2008
@@ -1,303 +1,303 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.junit.extensions;
-
-import junit.framework.TestCase;
-
-import org.apache.log4j.Logger;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-/**
- * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour.
- *
- * <p>ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal
- * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is
- * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure
- * for different numbers of data elements held in the data structure the int parameter should be interpreted as the
- * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour
- * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class
- * will also be run.
- *
- * <p>TestCases derived from this class may also define tear down methods to clean up their memory usage. This is
- * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is
- * to write a test that allocates memory in the main test method in such a way that it leaves that memory still
- * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method
- * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the
- * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of
- * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed.
- *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Store the current int parameter value. <td> {@link TKTestResult} and see {@link AsymptoticTestDecorator} too.
- * <tr><td> Invoke parameterized test methods.
- * </table>
- *
- * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or
- *       {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this
- *       class to do time and space performance analysis, these methods could be added to any JUnit TestCase class
- *       instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to
- *       work with this extension, and also tests that extend other junit extension classes could have parameterized
- *       and tear down methods too.
- *
- * @author Rupert Smith
- */
-public class AsymptoticTestCase extends TestCase implements InstrumentedTest
-{
-    /** Used for logging. */
-    private static final Logger log = Logger.getLogger(AsymptoticTestCase.class);
-
-    /** The name of the test case. */
-    private String testCaseName;
-
-    /** Thread local for holding measurements on a per thread basis. */
-    ThreadLocal<TestMeasurements> threadLocalMeasurement =
-        new ThreadLocal<TestMeasurements>()
-        {
-            /**
-             * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1).
-             *
-             * @return A set of default test measurements.
-             */
-            protected synchronized TestMeasurements initialValue()
-            {
-                return new TestMeasurements();
-            }
-        };
-
-    /**
-     * Constructs a test case with the given name.
-     *
-     * @param name The name of the test.
-     */
-    public AsymptoticTestCase(String name)
-    {
-        super(name);
-
-        log.debug("public AsymptoticTestCase(String " + name + "): called");
-        testCaseName = name;
-    }
-
-    /**
-     * Gets the current value of the integer parameter to be passed to the parameterized test.
-     *
-     * @return The current value of the integer parameter.
-     */
-    public int getN()
-    {
-        log.debug("public int getN(): called");
-        int n = threadLocalMeasurement.get().n;
-
-        log.debug("return: " + n);
-
-        return n;
-    }
-
-    /**
-     * Sets the current value of the integer parameter to be passed to the parameterized test.
-     *
-     * @param n The new current value of the integer parameter.
-     */
-    public void setN(int n)
-    {
-        log.debug("public void setN(int " + n + "): called");
-        threadLocalMeasurement.get().n = n;
-    }
-
-    /**
-     * Reports how long the test took to run.
-     *
-     * @return The time in milliseconds that the test took to run.
-     */
-    public long getTestTime()
-    {
-        log.debug("public long getTestTime(): called");
-        long startTime = threadLocalMeasurement.get().startTime;
-        long endTime = threadLocalMeasurement.get().endTime;
-        long testTime = endTime - startTime;
-
-        log.debug("return: " + testTime);
-
-        return testTime;
-    }
-
-    /**
-     * Reports the memory usage at the start of the test.
-     *
-     * @return The memory usage at the start of the test.
-     */
-    public long getTestStartMemory()
-    {
-        // log.debug("public long getTestStartMemory(): called");
-        long startMem = threadLocalMeasurement.get().startMem;
-
-        // log.debug("return: " + startMem);
-
-        return startMem;
-    }
-
-    /**
-     * Reports the memory usage at the end of the test.
-     *
-     * @return The memory usage at the end of the test.
-     */
-    public long getTestEndMemory()
-    {
-        // log.debug("public long getTestEndMemory(): called");
-        long endMem = threadLocalMeasurement.get().endMem;
-
-        // log.debug("return: " + endMem);
-        return endMem;
-    }
-
-    /**
-     * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory
-     * can be reclaimed.
-     */
-    public void reset()
-    {
-        log.debug("public void reset(): called");
-        threadLocalMeasurement.remove();
-    }
-
-    /**
-     * Runs the test method for this test case.
-     *
-     * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through.
-     */
-    protected void runTest() throws Throwable
-    {
-        log.debug("protected void runTest(): called");
-
-        // Check that a test name has been set. This is used to define which method to run.
-        assertNotNull(testCaseName);
-        log.debug("testCaseName = " + testCaseName);
-
-        // Try to get the method with matching name.
-        Method runMethod = null;
-        boolean isParameterized = false;
-
-        // Check if a parameterized test method is available.
-        try
-        {
-            // Use getMethod to get all public inherited methods. getDeclaredMethods returns all
-            // methods of this class but excludes the inherited ones.
-            runMethod = getClass().getMethod(testCaseName, int.class);
-            isParameterized = true;
-        }
-        catch (NoSuchMethodException e)
-        {
-            // log.debug("Parameterized method \"" + testCaseName + "\" not found.");
-            // Set run method to null (it already will be but...) to indicate that no parameterized method
-            // version could be found.
-            runMethod = null;
-        }
-
-        // If no parameterized method is available, try and get the unparameterized method.
-        if (runMethod == null)
-        {
-            try
-            {
-                runMethod = getClass().getMethod(testCaseName);
-                isParameterized = false;
-
-            }
-            catch (NoSuchMethodException e)
-            {
-                fail("Method \"" + testCaseName + "\" not found.");
-            }
-        }
-
-        // Check that the method is publicly accessable.
-        if (!Modifier.isPublic(runMethod.getModifiers()))
-        {
-            fail("Method \"" + testCaseName + "\" should be public.");
-        }
-
-        // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or
-        // resulting exceptions from the method to fall through.
-        try
-        {
-            Integer paramN = getN();
-            log.debug("paramN = " + paramN);
-
-            // Calculate parameters for parameterized tests so new does not get called during memory measurement.
-            Object[] params = new Object[] { paramN };
-
-            // Take the test start memory and start time.
-            threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory();
-
-            threadLocalMeasurement.get().startTime = System.nanoTime();
-
-            if (isParameterized)
-            {
-                runMethod.invoke(this, params);
-            }
-            else
-            {
-                runMethod.invoke(this);
-            }
-        }
-        catch (InvocationTargetException e)
-        {
-            e.fillInStackTrace();
-            throw e.getTargetException();
-        }
-        catch (IllegalAccessException e)
-        {
-            e.fillInStackTrace();
-            throw e;
-        }
-        finally
-        {
-            // Take the test end memory and end time and calculate how long it took to run.
-            long endTime = System.nanoTime();
-            threadLocalMeasurement.get().endTime = endTime;
-            log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = "
-                + getTestTime());
-
-            threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory();
-        }
-    }
-
-    /**
-     * The test parameters, encapsulated as a unit for attaching on a per thread basis.
-     */
-    private static class TestMeasurements
-    {
-        /** Holds the current value of the integer parameter to run tests on. */
-        public int n = 1;
-
-        /** Holds the test start memory. */
-        public long startTime = 0;
-
-        /** Holds the test end memory. */
-        public long endTime = 0;
-
-        /** Holds the test start memory. */
-        public long startMem = 0;
-
-        /** Holds the test end memory. */
-        public long endMem = 0;
-    }
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour.
+ *
+ * <p>ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal
+ * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is
+ * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure
+ * for different numbers of data elements held in the data structure the int parameter should be interpreted as the
+ * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour
+ * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class
+ * will also be run.
+ *
+ * <p>TestCases derived from this class may also define tear down methods to clean up their memory usage. This is
+ * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is
+ * to write a test that allocates memory in the main test method in such a way that it leaves that memory still
+ * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method
+ * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the
+ * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of
+ * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Store the current int parameter value. <td> {@link TKTestResult} and see {@link AsymptoticTestDecorator} too.
+ * <tr><td> Invoke parameterized test methods.
+ * </table>
+ *
+ * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or
+ *       {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this
+ *       class to do time and space performance analysis, these methods could be added to any JUnit TestCase class
+ *       instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to
+ *       work with this extension, and also tests that extend other junit extension classes could have parameterized
+ *       and tear down methods too.
+ *
+ * @author Rupert Smith
+ */
+public class AsymptoticTestCase extends TestCase implements InstrumentedTest
+{
+    /** Used for logging. */
+    private static final Logger log = Logger.getLogger(AsymptoticTestCase.class);
+
+    /** The name of the test case. */
+    private String testCaseName;
+
+    /** Thread local for holding measurements on a per thread basis. */
+    ThreadLocal<TestMeasurements> threadLocalMeasurement =
+        new ThreadLocal<TestMeasurements>()
+        {
+            /**
+             * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1).
+             *
+             * @return A set of default test measurements.
+             */
+            protected synchronized TestMeasurements initialValue()
+            {
+                return new TestMeasurements();
+            }
+        };
+
+    /**
+     * Constructs a test case with the given name.
+     *
+     * @param name The name of the test.
+     */
+    public AsymptoticTestCase(String name)
+    {
+        super(name);
+
+        log.debug("public AsymptoticTestCase(String " + name + "): called");
+        testCaseName = name;
+    }
+
+    /**
+     * Gets the current value of the integer parameter to be passed to the parameterized test.
+     *
+     * @return The current value of the integer parameter.
+     */
+    public int getN()
+    {
+        log.debug("public int getN(): called");
+        int n = threadLocalMeasurement.get().n;
+
+        log.debug("return: " + n);
+
+        return n;
+    }
+
+    /**
+     * Sets the current value of the integer parameter to be passed to the parameterized test.
+     *
+     * @param n The new current value of the integer parameter.
+     */
+    public void setN(int n)
+    {
+        log.debug("public void setN(int " + n + "): called");
+        threadLocalMeasurement.get().n = n;
+    }
+
+    /**
+     * Reports how long the test took to run.
+     *
+     * @return The time in milliseconds that the test took to run.
+     */
+    public long getTestTime()
+    {
+        log.debug("public long getTestTime(): called");
+        long startTime = threadLocalMeasurement.get().startTime;
+        long endTime = threadLocalMeasurement.get().endTime;
+        long testTime = endTime - startTime;
+
+        log.debug("return: " + testTime);
+
+        return testTime;
+    }
+
+    /**
+     * Reports the memory usage at the start of the test.
+     *
+     * @return The memory usage at the start of the test.
+     */
+    public long getTestStartMemory()
+    {
+        // log.debug("public long getTestStartMemory(): called");
+        long startMem = threadLocalMeasurement.get().startMem;
+
+        // log.debug("return: " + startMem);
+
+        return startMem;
+    }
+
+    /**
+     * Reports the memory usage at the end of the test.
+     *
+     * @return The memory usage at the end of the test.
+     */
+    public long getTestEndMemory()
+    {
+        // log.debug("public long getTestEndMemory(): called");
+        long endMem = threadLocalMeasurement.get().endMem;
+
+        // log.debug("return: " + endMem);
+        return endMem;
+    }
+
+    /**
+     * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory
+     * can be reclaimed.
+     */
+    public void reset()
+    {
+        log.debug("public void reset(): called");
+        threadLocalMeasurement.remove();
+    }
+
+    /**
+     * Runs the test method for this test case.
+     *
+     * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through.
+     */
+    protected void runTest() throws Throwable
+    {
+        log.debug("protected void runTest(): called");
+
+        // Check that a test name has been set. This is used to define which method to run.
+        assertNotNull(testCaseName);
+        log.debug("testCaseName = " + testCaseName);
+
+        // Try to get the method with matching name.
+        Method runMethod = null;
+        boolean isParameterized = false;
+
+        // Check if a parameterized test method is available.
+        try
+        {
+            // Use getMethod to get all public inherited methods. getDeclaredMethods returns all
+            // methods of this class but excludes the inherited ones.
+            runMethod = getClass().getMethod(testCaseName, int.class);
+            isParameterized = true;
+        }
+        catch (NoSuchMethodException e)
+        {
+            // log.debug("Parameterized method \"" + testCaseName + "\" not found.");
+            // Set run method to null (it already will be but...) to indicate that no parameterized method
+            // version could be found.
+            runMethod = null;
+        }
+
+        // If no parameterized method is available, try and get the unparameterized method.
+        if (runMethod == null)
+        {
+            try
+            {
+                runMethod = getClass().getMethod(testCaseName);
+                isParameterized = false;
+
+            }
+            catch (NoSuchMethodException e)
+            {
+                fail("Method \"" + testCaseName + "\" not found.");
+            }
+        }
+
+        // Check that the method is publicly accessable.
+        if (!Modifier.isPublic(runMethod.getModifiers()))
+        {
+            fail("Method \"" + testCaseName + "\" should be public.");
+        }
+
+        // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or
+        // resulting exceptions from the method to fall through.
+        try
+        {
+            Integer paramN = getN();
+            log.debug("paramN = " + paramN);
+
+            // Calculate parameters for parameterized tests so new does not get called during memory measurement.
+            Object[] params = new Object[] { paramN };
+
+            // Take the test start memory and start time.
+            threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory();
+
+            threadLocalMeasurement.get().startTime = System.nanoTime();
+
+            if (isParameterized)
+            {
+                runMethod.invoke(this, params);
+            }
+            else
+            {
+                runMethod.invoke(this);
+            }
+        }
+        catch (InvocationTargetException e)
+        {
+            e.fillInStackTrace();
+            throw e.getTargetException();
+        }
+        catch (IllegalAccessException e)
+        {
+            e.fillInStackTrace();
+            throw e;
+        }
+        finally
+        {
+            // Take the test end memory and end time and calculate how long it took to run.
+            long endTime = System.nanoTime();
+            threadLocalMeasurement.get().endTime = endTime;
+            log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = "
+                + getTestTime());
+
+            threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory();
+        }
+    }
+
+    /**
+     * The test parameters, encapsulated as a unit for attaching on a per thread basis.
+     */
+    private static class TestMeasurements
+    {
+        /** Holds the current value of the integer parameter to run tests on. */
+        public int n = 1;
+
+        /** Holds the test start memory. */
+        public long startTime = 0;
+
+        /** Holds the test end memory. */
+        public long endTime = 0;
+
+        /** Holds the test start memory. */
+        public long startMem = 0;
+
+        /** Holds the test end memory. */
+        public long endMem = 0;
+    }
+}

Propchange: incubator/qpid/trunk/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native