/* * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/** * RacingThreadsTest is a support class for creating a test * where multiple threads are needed to exercise a code path. * The RacingThreadsTest class is typically used as follows: * <ul> * <li> * Extend RacingThreadsTest class in order to provide the test * specific variables and/or code, e.g., <br> * public class MyRacingThreadsTest extends RacingThreadsTest * <li> * Use * "new MyRacingThreadsTest(name, n_threads, n_loops, n_secs)" * to create your test with the specified name and the specified * number of WorkerThreads that execute the test code in parallel * up to n_loops iterations or n_secs seconds. * <li> * Use * "new DriverThread(test)" * to create the test DriverThread that manages all the * WorkerThreads. The DriverThread class can be extended to * provide test specific code and/or variables. However, that * is typically done in your test's subclass. * <li> * Use * "new WorkerThread(workerNum, test)" * to create WorkerThread-workerNum that executes the test code. * The WorkerThread class can be extended to provide test thread * specific code and/or variables. * <li> * Use * "RacingThreadsTest.runTest(driver, workers)" * to run the test. If the test fails, then a RuntimeException * is thrown. * </ul> * * The RacingThreadsTest class provides many methods that can be * overridden in order to provide test specific semantics at each * identified test execution point. At a minimum, your test's * subclass needs to override the * "void executeRace(WorkerThread)" * method in order to exercise your race condition and it needs to * override the * "void checkRaceResults(DriverThread)" * method in order to check the results of the race. Your * checkRaceResults() method should call the * "int incAndGetFailCnt()" * method when it detects a failure. It can also call the * "void unexpectedException(Thread, Exception)" * method if it detects an unexpected exception; this will cause * an error message to be output and the failure count to be * incremented. When the RacingThreadsTest.runTest() method is * done running the races, if there is a non-zero failure count, * then a RuntimeException will be thrown. * <p> * The RacingThreadsTest class uses three internal barriers to * coordinate actions between the DriverThread and the WorkerThreads. * These barriers should not be managed or used by your test's * subclass and are only mentioned here to provide clarity about * interactions between the DriverThread and the WorkerThreads. * The following transaction diagram shows when the different * RacingThreadsTest methods are called relative to the different * barriers: * * <pre> * DriverThread WorkerThread-0 WorkerThread-N-1 * --------------------- --------------------- --------------------- * run(workers) * oneTimeDriverInit() * <start WorkerThreads> run() run() * <top of race loop> : : * perRaceDriverInit() oneTimeWorkerInit() oneTimeWorkerInit() * : <top of race loop> <top of race loop> * : perRaceWorkerInit() perRaceWorkerInit() * startBarrier startBarrier startBarrier * : executeRace() executeRace() * finishBarrier finishBarrier finishBarrier * checkRaceResults() : : * resetBarrier resetBarrier resetBarrier * perRaceDriverEpilog() perRaceWorkerEpilog() perRaceWorkerEpilog() * <repeat race or done> <repeat race or done> <repeat race or done> * : oneTimeWorkerEpilog() oneTimeWorkerEpilog() * <join WorkerThreads> <WorkerThread ends> <WorkerThread ends> * oneTimeDriverEpilog() * <DriverThread ends> * </pre> * * Just to be clear about the parallel parts of this infrastructure: * <ul> * <li> * After the DriverThread starts the WorkerThreads, the DriverThread * and the WorkerThreads are running in parallel until the startBarrier * is reached. * <li> * After the WorkerThreads leave the startBarrier, they are running * the code in executeRace() in parallel which is the whole point * of this class. * <li> * The DriverThread heads straight to the finishBarrier and waits for * the WorkerThreads to get there. * <li> * After the DriverThread leaves the finishBarrier, it checks the * results of the race. * <li> * The WorkerThreads head straight to the resetBarrier and wait for * the DriverThread to get there. * <li> * If this is not the last race, then after the DriverThread and * WorkerThreads leave the resetBarrier, the DriverThread and the * WorkerThreads are running in parallel until the startBarrier * is reached. * <li> * If this is the last race, then after the DriverThread and * WorkerThreads leave the resetBarrier, the DriverThread and the * WorkerThreads are running in parallel as each WorkerThread ends. * <li> * The DriverThread waits for the WorkerThreads to end and * then it ends * </ul> * * Once the DriverThread has ended, the RacingThreadsTest.runTest() * method checks the failure count. If there were no failures, then * a "Test PASSed" message is printed. Otherwise, the failure count * is printed, a "Test FAILed" message is printed and a RuntimeException * is thrown.
*/ publicclass RacingThreadsTest { /** * name of the test
*/ publicfinal String TEST_NAME; /** * maximum number of test iterations (race loops)
*/ publicfinalint N_LOOPS; /** * the maximum number of seconds to execute the test loop
*/ publicfinalint N_SECS; /** * number of WorkerThreads
*/ publicfinalint N_THREADS;
/** * Creates a test with the specified name and the specified number * of WorkerThreads that execute the test code in parallel up to * n_loops iterations or n_secs seconds. The RacingThreadsTest * class is extended in order to provide the test specific variables * and/or code. * @param name the name of the test * @param n_threads the number of WorkerThreads * @param n_loops the maximum number of test iterations * @param n_secs the maximum number of seconds to execute the test loop
*/
RacingThreadsTest(String name, int n_threads, int n_loops, int n_secs) {
TEST_NAME = name;
N_THREADS = n_threads;
N_LOOPS = n_loops;
N_SECS = n_secs;
finishBarrier = new CyclicBarrier(N_THREADS + 1);
resetBarrier = new CyclicBarrier(N_THREADS + 1);
startBarrier = new CyclicBarrier(N_THREADS + 1);
}
/** * Entry point for exercising the RacingThreadsTest class.
*/ publicstaticvoid main(String[] args) { // a dummy test: // - 2 threads // - 3 loops // - 2 seconds // - standard DriverThread // - standard WorkerThread
RacingThreadsTest test = new RacingThreadsTest("dummy", 2, 3, 2);
DriverThread driver = new DriverThread(test);
WorkerThread[] workers = new WorkerThread[2]; for (int i = 0; i < workers.length; i++) {
workers[i] = new WorkerThread(i, test);
}
test.runTest(driver, workers);
}
privatestaticvolatileboolean done = false; // test done flag
// # of fails; AtomicInteger since any WorkerThread can increment privatestaticfinal AtomicInteger failCnt = new AtomicInteger(); // # of loops; volatile is OK since only DriverThread increments // but using AtomicInteger for consistency privatestaticfinal AtomicInteger loopCnt = new AtomicInteger(); privatestaticboolean verbose
= Boolean.getBoolean("RacingThreadsTest.verbose");
// barriers for starting, finishing and resetting the race privatefinal CyclicBarrier finishBarrier; privatefinal CyclicBarrier resetBarrier; privatefinal CyclicBarrier startBarrier;
/** * Get current done flag value. * @return the current done flag value
*/ publicboolean getDone() { return done;
}
/** * Set done flag to specified value. * @param v the new done flag value
*/ publicvoid setDone(boolean v) {
done = v;
}
/** * Get current failure counter value. * @return the current failure count
*/ publicint getFailCnt() { return failCnt.get();
}
/** * Increment and get current failure counter value. * @return the current failure count after incrementing
*/ publicint incAndGetFailCnt() { return failCnt.incrementAndGet();
}
/** * Get current loop counter value. * @return the current loop count
*/ publicint getLoopCnt() { return loopCnt.get();
}
/** * Increment and get current loop counter value. * @return the current loop count after incrementing
*/ publicint incAndGetLoopCnt() { return loopCnt.incrementAndGet();
}
/** * Get current verbose flag value. * @return the current verbose flag value
*/ publicboolean getVerbose() { return verbose;
}
/** * Set verbose flag to specified value. * @param v the new verbose flag value
*/ publicvoid setVerbose(boolean v) {
verbose = v;
}
/** * Run the test with the specified DriverThread and the * specified WorkerThreads. * @param driver the DriverThread for running the test * @param workers the WorkerThreads for executing the race * @exception RuntimeException the test has failed
*/ publicvoid runTest(DriverThread driver, WorkerThread[] workers) {
driver.run(workers);
try {
driver.join();
} catch (InterruptedException ie) {
unexpectedException(Thread.currentThread(), ie); // fall through to test failed below
}
if (failCnt.get() == 0) {
System.out.println(TEST_NAME + ": Test PASSed.");
} else {
System.out.println(TEST_NAME + ": failCnt=" + failCnt.get());
System.out.println(TEST_NAME + ": Test FAILed."); thrownew RuntimeException("Test Failed");
}
}
/** * Helper method for reporting an unexpected Exception and * calling incAndGetFailCnt(); * @param t the Thread that caught the exception * @param e the Exception that was caught
*/ publicvoid unexpectedException(Thread t, Exception e) {
System.err.println(t.getName() + ": ERROR: unexpected exception: " + e);
incAndGetFailCnt(); // ignore return
}
// The following methods are typically overridden by the subclass // of RacingThreadsTest to provide test specific semantics at each // identified test execution point:
/** * Initialize 1-time items for the DriverThread. * Called by the DriverThread before WorkerThreads are started. * @param dt the DriverThread
*/ publicvoid oneTimeDriverInit(DriverThread dt) { if (verbose)
System.out.println(dt.getName() + ": oneTimeDriverInit() called");
}
/** * Initialize 1-time items for a WorkerThread. Called by a * WorkerThread after oneTimeDriverInit() and before the * WorkerThread checks in with startBarrier. May execute in * parallel with perRaceDriverInit() or with another * WorkerThread's oneTimeWorkerInit() call or another * WorkerThread's perRaceWorkerInit() call. * @param wt the WorkerThread
*/ publicvoid oneTimeWorkerInit(WorkerThread wt) { if (verbose)
System.out.println(wt.getName() + ": oneTimeWorkerInit() called");
}
/** * Initialize per-race items for the DriverThread. Called by the * DriverThread before it checks in with startBarrier. May execute * in parallel with oneTimeWorkerInit() and perRaceWorkerInit() * calls. After any race except for the last race, this method may * execute in parallel with perRaceWorkerEpilog(). * @param dt the DriverThread
*/ publicvoid perRaceDriverInit(DriverThread dt) { if (verbose)
System.out.println(dt.getName() + ": perRaceDriverInit() called");
}
/** * Initialize per-race items for a WorkerThread. Called by each * WorkerThread before it checks in with startBarrier. On the first * call, this method may execute in parallel with another * WorkerThread's oneTimeWorkerInit() call. On any call, this method * may execute in parallel with perRaceDriverInit() or another * WorkerThread's perRaceWorkerInit() call. After any race except * for the last race, this method may execute in parallel with * perRaceDriverEpilog() or another WorkerThread's * perRaceWorkerEpilog() call. * @param wt the WorkerThread
*/ publicvoid perRaceWorkerInit(WorkerThread wt) { if (verbose)
System.out.println(wt.getName() + ": perRaceWorkerInit() called");
}
/** * Execute the race in a WorkerThread. Called by each WorkerThread * after it has been released from startBarrier. * @param wt the WorkerThread
*/ publicvoid executeRace(WorkerThread wt) { if (verbose)
System.out.println(wt.getName() + ": executeRace() called");
}
/** * Check race results in the DriverThread. Called by the DriverThread * after it has been released from finishBarrier and before the * DriverThread checks in with resetBarrier. * @param dt the DriverThread
*/ publicvoid checkRaceResults(DriverThread dt) { if (verbose)
System.out.println(dt.getName() + ": checkRaceResults() called");
}
/** * Handle end-of-race items for the DriverThread. Called by the * DriverThread after it has been released from resetBarrier and * before the DriverThread checks in again with startBarrier. Can * execute in parallel with perRaceWorkerEpilog(). If this is not * the last race, can execute in parallel with perRaceWorkerInit(). * If this is the last race, can execute in parallel with * oneTimeWorkerEpilog(). * @param dt the DriverThread
*/ publicvoid perRaceDriverEpilog(DriverThread dt) { if (verbose)
System.out.println(dt.getName() + ": perRaceDriverEpilog() called");
}
/** * Handle end-of-race items for a WorkerThread. Called by each * WorkerThread after it has been released from resetBarrier and * before the WorkerThread checks in again with startBarrier. * Can execute in parallel with perRaceDriverEpilog() or another * WorkerThread's perRaceWorkerEpilog() call. If this is not the * last race, can execute in parallel with perRaceDriverInit(), * or another WorkerThread's perRaceWorkerInit() call. If this * is the last race, can execute in parallel with another * WorkerThread's oneTimeWorkerEpilog() call. * @param wt the WorkerThread
*/ publicvoid perRaceWorkerEpilog(WorkerThread wt) { if (verbose)
System.out.println(wt.getName() + ": perRaceWorkerEpilog() called");
}
/** * Handle end-of-test items for a WorkerThread. Called by each * WorkerThread after it has detected that all races are done and * before oneTimeDriverEpilog() is called. Can execute in parallel * with perRaceDriverEpilog(), with another WorkerThread's * perRaceWorkerEpilog() call or with another WorkerThread's * oneTimeWorkerEpilog() call. * @param wt the WorkerThread
*/ publicvoid oneTimeWorkerEpilog(WorkerThread wt) { if (verbose)
System.out.println(wt.getName() + ": oneTimeWorkerEpilog() called");
}
/** * Handle end-of-test items for the DriverThread. Called by the * DriverThread after all the WorkerThreads have called * oneTimeWorkerEpilog(). * @param dt the DriverThread
*/ publicvoid oneTimeDriverEpilog(DriverThread dt) { if (verbose)
System.out.println(dt.getName() + ": oneTimeDriverEpilog() called");
}
/** * DriverThread for executing the test.
*/ publicstaticclass DriverThread extendsThread { privatefinal RacingThreadsTest test;
/** * Create the test DriverThread that manages all the WorkerThreads. * The DriverThread class can be extended to provide test specific * variables and/or code. However, that is typically done in the * subclass of RacingThreadsTest. * @parameter test the RacingThreadsTest being run
*/
DriverThread(RacingThreadsTest test) { super("DriverThread"); this.test = test;
}
// initialize per-race items for the DriverThread
test.perRaceDriverInit(this);
try { // we've setup the race so start it when all // WorkerThreads get to the startBarrier
test.startBarrier.await();
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe); return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie); return;
}
// All WorkerThreads are racing via executeRace() // at this point
// wait for all threads to finish the race try {
test.finishBarrier.await();
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe); return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie); return;
} // All WorkerThreads are heading to resetBarrier at this // point so we can check the race results before we reset // for another race (or bail because we are done).
test.checkRaceResults(this);
if (test.getLoopCnt() + 1 >= test.N_LOOPS ||
System.currentTimeMillis() >= endTime) { // This is the last loop or we're out of time. // Let test threads know we are done before we release // them from resetBarrier
test.setDone(true);
}
// All WorkerThreads call perRaceWorkerEpilog(). If // this is not the last loop, then all WorkerThreads // will also call perRaceWorkerInit() on the way to // startBarrier. If this is the last loop, then all // WorkerThreads will call oneTimeWorkerEpilog() on // their way to ending.
// handle end-of-race items for the DriverThread
test.perRaceDriverEpilog(this);
}
for (int i = 0; i < workers.length; i++) { try {
workers[i].join();
} catch (InterruptedException ie) {
test.unexpectedException(this, ie); return;
}
}
// handle end-of-test items for the DriverThread
test.oneTimeDriverEpilog(this);
System.out.println(getName() + ": is done.");
}
}
/** * WorkerThread for executing the race.
*/ publicstaticclass WorkerThread extendsThread { privatefinal RacingThreadsTest test; privatefinalint workerNum;
/** * Creates WorkerThread-N that executes the test code. The * WorkerThread class can be extended to provide test thread * specific variables and/or code. * @param workerNum the number for the new WorkerThread * @parameter test the RacingThreadsTest being run
*/
WorkerThread(int workerNum, RacingThreadsTest test) { super("WorkerThread-" + workerNum); this.test = test; this.workerNum = workerNum;
}
/** * get the WorkerThread's number * @return the WorkerThread's number
*/ publicint getWorkerNum() { return workerNum;
}
/** * Run the race in a WorkerThread.
*/ publicvoid run() {
System.out.println(getName() + ": is running.");
// initialize 1-time items for the WorkerThread
test.oneTimeWorkerInit(this);
while (!test.getDone()) { // initialize per-race items for the WorkerThread
test.perRaceWorkerInit(this);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.