All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.thesett.junit.concurrency.TestRunnable Maven / Gradle / Ivy

Go to download

JUnit Toolkit enhances JUnit with performance testing, asymptotic behaviour analysis, and concurrency testing.

There is a newer version: 0.9.52
Show newest version
/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed 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 com.thesett.junit.concurrency;

/**
 * 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 {@link ThreadTestCoordinator}. It is called through the * convenience methods {@link #allow} and {@link #waitFor}. 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 {@link #waitFor} * 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. *
* * @author Rupert Smith * @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. */ public abstract class TestRunnable implements Runnable { /** Holds a reference to the thread coordinator. */ private ThreadTestCoordinator coordinator; /** Holds the explicit integer id of this thread. */ private int id; /** Used to indicate that this thread is waiting on the coordinator and not elsewhere. */ private boolean waitingOnCoordinator = false; /** Used to accumulate error messsages. */ private String errorMessage = ""; /** Holds the Java thread object that this is running under. */ private Thread thisThread; /** Used to hold any exceptions resulting from the run method. */ private Exception runException = null; /** * Implementations override this to perform coordinated thread sequencing. * * @throws Exception Any exception raised by the implementation will be caught by the default {@link #run()} * implementation for later querying by the {@link #getException()} method. */ public abstract void runWithExceptions() throws Exception; /** * Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record of * those exceptions. Defers to the {@link #runWithExceptions()} method to provide the thread body implementation and * catches any exceptions thrown by it. */ public void run() { try { runWithExceptions(); } catch (Exception e) { this.runException = e; } } /** * Provides a string summary of this test threads status. * * @return Summarizes this threads status. */ public String toString() { return "id = " + id + ", waitingOnCoordinator = " + waitingOnCoordinator; } /** * Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs. * * @param threads The set of threads that can allow this one to continue. * @param 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. * * @return 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. */ protected boolean waitFor(int[] threads, boolean otherWaitIsAllow) { return coordinator.consumeAllowEvent(threads, otherWaitIsAllow, id, this); } /** * Produces allow events on each of the specified threads. * * @param threads The set of threads that are to be allowed to continue. */ protected void allow(int[] threads) { coordinator.produceAllowEvents(threads, id, this); } /** * Keeps the error message for later reporting by the coordinator. * * @param message The error message to keep. */ protected void addErrorMessage(String message) { errorMessage += message; } /** * Sets the coordinator for this thread. * * @param coordinator The coordinator for this thread. */ void setCoordinator(ThreadTestCoordinator coordinator) { this.coordinator = coordinator; } /** * Reports whether or not this thread is waiting on the coordinator. * * @return If this thread is waiting on the coordinator. */ boolean isWaitingOnCoordinator() { return waitingOnCoordinator; } /** * Sets the value of the waiting on coordinator flag. * * @param waiting The value of the waiting on coordinator flag. */ void setWaitingOnCoordinator(boolean waiting) { waitingOnCoordinator = waiting; } /** * Sets up the explicit int id for this thread. * * @param id The integer id. */ void setId(int id) { this.id = id; } /** * Reports any accumulated error messages. * * @return Any accumulated error messages. */ String getErrorMessage() { return errorMessage; } /** * Reports any exception thrown by the {@link #runWithExceptions} method. * * @return Any exception thrown by the {@link #runWithExceptions} method. */ Exception getException() { return runException; } /** * Sets the Java thread under which this runs. * * @param thread The Java thread under which this runs. */ void setThread(Thread thread) { thisThread = thread; } /** * Gets the Java thread under which this runs. * * @return The Java thread under which this runs. */ Thread getThread() { return thisThread; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy