/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.util.test; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.ArrayList; import org.jboss.util.threadpool.BasicThreadPool; import org.jboss.util.threadpool.ThreadPoolFullException; import org.jboss.util.threadpool.BlockingMode; import org.jboss.logging.Logger; import junit.framework.TestCase; /** * Tests of thread pool with Runnables added to the pool * * @see org.jboss.util.threadpool.ThreadPool * @author <a href="adrian@jboss.org">Adrian.Brock</a> * @author Scott.Stark@jboss.org * @version $Revision: 61635 $ */ public class ThreadPoolRunnableUnitTestCase extends TestCase { private static Logger log = Logger.getLogger(ThreadPoolRunnableUnitTestCase.class); /** Basic test */ static final int BASIC = 0; /** Hold the thread after start */ static final int HOLD_START = 1; /** The started runnables */ HashSet startedRunnables = new HashSet(); /** The started releases */ HashSet startedReleases = new HashSet(); /** The finished runnables */ HashSet finishedRunnables = new HashSet(); /** The thread names */ HashMap threadNames = new HashMap(); /** * Create a new ThreadPoolRunnableUnitTestCase * * @param name the test to run */ public ThreadPoolRunnableUnitTestCase(String name) { super(name); } /** * Basic test */ public void testBasic() throws Exception { log.debug("testBasic"); BasicThreadPool pool = new BasicThreadPool(); try { pool.run(new TestRunnable(BASIC, "test")); waitFinished(1); HashSet expected = makeExpected(new Object[] {"test"}); assertEquals(expected, finishedRunnables); } finally { pool.stop(true); } } /** * Multiple Basic test */ public void testMultipleBasic() throws Exception { log.debug("testMultipleBasic"); BasicThreadPool pool = new BasicThreadPool(); try { pool.run(new TestRunnable(BASIC, "test1")); pool.run(new TestRunnable(BASIC, "test2")); pool.run(new TestRunnable(BASIC, "test3")); waitFinished(3); HashSet expected = makeExpected(new Object[] {"test1", "test2", "test3"}); assertEquals(expected, finishedRunnables); } finally { pool.stop(true); } } /** * Test pooling */ public void testSimplePooling() throws Exception { log.debug("testSimplePooling"); BasicThreadPool pool = new BasicThreadPool(); pool.setMaximumPoolSize(1); try { pool.run(new TestRunnable(BASIC, "test1")); waitFinished(1); pool.run(new TestRunnable(BASIC, "test2")); waitFinished(2); assertEquals(threadNames.get("test1"), threadNames.get("test2")); } finally { pool.stop(true); } } /** * Test multiple pooling */ public void testMultiplePooling() throws Exception { log.debug("testMultiplePooling"); BasicThreadPool pool = new BasicThreadPool(); try { pool.run(new TestRunnable(HOLD_START, "test1")); waitStarted(1); pool.run(new TestRunnable(BASIC, "test2")); waitFinished(1); releaseStarted("test1"); waitFinished(2); assertTrue("Shouldn't run on the same thread", threadNames.get("test1").equals(threadNames.get("test2")) == false); } finally { pool.stop(true); } } /** * Test maximum pool */ public void testMaximumPool() throws Exception { log.debug("testMaximumPool"); BasicThreadPool pool = new BasicThreadPool(); pool.setMaximumPoolSize(1); try { pool.run(new TestRunnable(HOLD_START, "test1")); waitStarted(1); pool.run(new TestRunnable(BASIC, "test2")); Thread.sleep(1000); assertEquals(0, finishedRunnables.size()); releaseStarted("test1"); waitFinished(2); assertEquals(makeExpected(new Object[] {"test1", "test2"}), finishedRunnables); } finally { pool.stop(true); } } /** * Test maximum cache */ public void testMaximumQueue() throws Exception { log.debug("testMaximumQueue"); BasicThreadPool pool = new BasicThreadPool(); pool.setMaximumQueueSize(1); pool.setMaximumPoolSize(1); try { pool.run(new TestRunnable(HOLD_START, "test1")); waitStarted(1); pool.run(new TestRunnable(BASIC, "test2")); boolean caught = false; try { pool.run(new TestRunnable(BASIC, "test3")); } catch (ThreadPoolFullException expected) { caught = true; } assertTrue("Expected ThreadPoolFullException", caught); releaseStarted("test1"); waitFinished(2); assertEquals(makeExpected(new Object[] {"test1", "test2"}), finishedRunnables); } finally { pool.stop(true); } } /** * Test runnable timeouts */ public void testRunnableTimeout() throws Exception { log.debug("testRunnableTimeout"); BasicThreadPool pool = new BasicThreadPool(); pool.setMaximumQueueSize(1); pool.setMaximumPoolSize(1); try { TestRunnable test = new TestRunnable(HOLD_START, "test1", 12*1000); pool.run(test, 0, 10*1000); waitStarted(1); releaseStarted("test1"); waitFinished(1); assertEquals(makeExpected(new Object[] {"test1"}), finishedRunnables); } finally { pool.stop(true); } } /** * Test runnable timeouts */ public void testRunnableTimeoutWithSpinLoop() throws Exception { log.debug("testRunnableTimeoutWithSpinLoop"); BasicThreadPool pool = new BasicThreadPool(); pool.setMaximumQueueSize(1); pool.setMaximumPoolSize(1); try { TestRunnable test = new TestRunnable(HOLD_START, "test1", Long.MAX_VALUE); pool.run(test, 0, 8*1000); waitStarted(1); releaseStarted("test1"); Thread.sleep(12*1000); // Run another task to validate the previous thread has been cleared pool.run(new TestRunnable(BASIC, "test2")); waitStarted(1); releaseStarted("test2"); waitFinished(1); assertEquals(makeExpected(new Object[] {"test2"}), finishedRunnables); } finally { pool.stop(true); } } /** * Test runnable timeouts */ public void testRunnableTimeoutWithSpinLoop2() throws Exception { log.debug("testRunnableTimeoutWithSpinLoop2"); BasicThreadPool pool = new BasicThreadPool(); pool.setMaximumQueueSize(1); pool.setMaximumPoolSize(1); pool.setBlockingMode(BlockingMode.RUN); try { TestRunnable test = new TestRunnable(BASIC, "testx", Long.MAX_VALUE); pool.run(test, 0, 1*1000); // Run another task to validate the previous thread has been cleared ArrayList tmp = new ArrayList(); for(int n = 0; n < 10; n ++) { String name = "test"+n; pool.run(new TestRunnable(BASIC, name)); tmp.add(name); } Thread.sleep(3000); assertEquals(makeExpected(tmp.toArray()), finishedRunnables); } finally { pool.stop(true); } } /** * Save the thread name * * @param data the test data * @param name the thread name */ public synchronized void saveRunnableThreadName(String data, String name) { threadNames.put(data, name); } /** * Wait for expected starts */ public synchronized void waitStarted(int target) throws InterruptedException { log.info("waitStarted, target="+target); while (startedRunnables.size() < target) wait(); } /** * Release in waiting for start * * @param data the thread to start */ public synchronized void releaseStarted(String data) { log.info("releaseStarted, data="+data); startedReleases.add(data); notifyAll(); } /** * Wait for release started */ public synchronized void waitForReleaseStarted(String data) { try { log.info("waitForReleaseStarted, data="+data); while (startedReleases.contains(data) == false) wait(); } catch (InterruptedException ignored) { } } /** * Notify started */ public synchronized void notifyStarted(String data) { log.info("notifyStarted, data="+data); startedRunnables.add(data); notifyAll(); } /** * Clear started */ public synchronized void clearStarted() { log.info("clearStarted"); startedRunnables.clear(); } /** * Wait for expected finishes */ public synchronized void waitFinished(int target) throws InterruptedException { log.info("waitFinished, target="+target); while (finishedRunnables.size() < target) wait(); } /** * Notify finished */ public synchronized void notifyFinished(String data) { log.info("notifyFinished, data="+data); finishedRunnables.add(data); notifyAll(); } /** * Clear finished */ public synchronized void clearFinished() { log.info("clearFinished"); finishedRunnables.clear(); } /** * Make the expected result * * @param expected the results as an object array * @return the expected result */ public HashSet makeExpected(Object[] expected) { return new HashSet(Arrays.asList(expected)); } /** * Test runnable */ public class TestRunnable implements Runnable { /** The test to run */ private int test; /** The data for the test */ private String data; private long runSleepTime; /** * Create a new TestRunnable * * @param test the test * @param data the test data */ public TestRunnable(int test, String data) { this(test, data, 0); } public TestRunnable(int test, String data, long runSleepTime) { this.test = test; this.data = data; this.runSleepTime = runSleepTime; } /** * Runnable implementation */ public void run() { log.info("Begin run"); saveThreadName(); started(); if( runSleepTime > 0 ) { log.info("Begin spin loop"); if( runSleepTime == Long.MAX_VALUE ) { while( true ) ; } else { log.info("Begin sleep"); try { Thread.sleep(runSleepTime); } catch(InterruptedException e) { } } } finished(); log.info("End run"); } /** * Save the thread */ public void saveThreadName() { saveRunnableThreadName(data, Thread.currentThread().getName()); } /** * The test is finished */ public void started() { notifyStarted(data); if (test == HOLD_START) waitForReleaseStarted(data); } /** * The test is finished */ public void finished() { notifyFinished(data); } } }