You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by le...@apache.org on 2002/02/12 07:15:26 UTC

cvs commit: jakarta-avalon-excalibur/src/java/org/apache/avalon/excalibur/testcase LatchedThreadGroup.java

leif        02/02/11 22:15:26

  Added:       src/java/org/apache/avalon/excalibur/testcase
                        LatchedThreadGroup.java
  Log:
  Add class to make it easier to write multithreaded tests.
  
  Revision  Changes    Path
  1.1                  jakarta-avalon-excalibur/src/java/org/apache/avalon/excalibur/testcase/LatchedThreadGroup.java
  
  Index: LatchedThreadGroup.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.avalon.excalibur.testcase;
  
  import junit.framework.TestCase;
  
  import org.apache.avalon.excalibur.testcase.CascadingAssertionFailedError;
  
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  
  /**
   * This class is useful for writing MultiThreaded test cases where you need to perform
   *  multithreaded load testing on a component.
   * <p>
   * An instance of will create a block of threads of the specified size.  Each thread will be
   *  assigned to run a specified Runnable instance.  The threads will then all wait at a latch
   *  until the go method is called.  The go method will not return until all of the
   *  Runnables have completed.
   *
   * @author <a href="mailto:leif@silveregg.co.jp">Leif Mortenson</a>
   * @version $Id: LatchedThreadGroup.java,v 1.1 2002/02/12 06:15:26 leif Exp $
   */
  public class LatchedThreadGroup
      extends AbstractLogEnabled
  {
      private Thread[]  m_threads;
      private Object    m_semaphore = new Object();
      private int       m_startedCount;
      private boolean   m_latched;
      private int       m_completedCount;
      private int       m_getCount;
      private Throwable m_exception;
      
      /*---------------------------------------------------------------
       * Constructors
       *-------------------------------------------------------------*/
      /**
       * Creates a LatchedThreadGroup with a thread for each Runnable in the runnables array.
       */
      public LatchedThreadGroup(Runnable[] runnables)
      {
          int threadCount = runnables.length;
          m_threads = new Thread[threadCount];
          for (int i = 0; i < threadCount; i++)
          {
              m_threads[i] = new Runner( runnables[i], "Latched_Thread_" + i );
          }
      }
      
      /**
       * Creates a LatchedThreadGroup with threadCount threads each running runnable.
       */
      public LatchedThreadGroup( Runnable runnable, int threadCount )
      {
          m_threads = new Thread[threadCount];
          for (int i = 0; i < threadCount; i++)
          {
              m_threads[i] = new Runner( runnable, "Latched_Thread_" + i );
          }
      }
      
      /*---------------------------------------------------------------
       * Methods
       *-------------------------------------------------------------*/
      protected void resetMemory()
      {
          System.gc();
          System.gc();
          
          // Let the system settle down.
          try
          {
              Thread.sleep( 50 );
          }
          catch (InterruptedException e ) {}
          Runtime runtime = Runtime.getRuntime();
          getLogger().debug( "Memory: " + ( runtime.totalMemory() - runtime.freeMemory() ) );
      }
      
      /**
       * Causes all of the Runnables to start at the same instance.  This method will return
       *  once all of the Runnables have completed.
       *
       * @returns time, in milliseconds, that it took for all of the Runnables to complete.
       */
      public long go()
          throws Exception
      {
          // Start each of the threads.  They will block until the latch is released.  This is
          //  necessary because it takes some time for the threads to each allocate their required
          //  system resources and actually be ready to run.
          int threadCount = m_threads.length;
          for (int i = 0; i < threadCount; i++)
          {
              m_threads[i].start();
          }
          
          // Wait for all of the threads to start before starting to time the test
          synchronized(m_semaphore)
          {
              while ( m_startedCount < threadCount ) {
                  m_semaphore.wait();
              }
              
              // Start clean
              resetMemory();
              
              // Release the threads.
              m_latched = true;
              getLogger().debug( "Main thread released the test thread latch." );
              m_semaphore.notifyAll();
          }
          // Start timing
          long startTime = System.currentTimeMillis();
          
          // Wait for all of the threads to complete
          synchronized(m_semaphore)
          {
              getLogger().debug( "Waiting for test threads to all complete." );
              while ( m_completedCount < threadCount ) {
                  try
                  {
                      m_semaphore.wait();
                  }
                  catch ( InterruptedException e ) {}
              }
          }
          final long duration = System.currentTimeMillis() - startTime;
          getLogger().debug( "All test threads completed." );
          
          if ( m_exception != null )
          {
              throw new CascadingAssertionFailedError( "Exception in test thread.", m_exception );
          }
          return duration;
      }
      
      /*---------------------------------------------------------------
       * Inner Classes
       *-------------------------------------------------------------*/
      private class Runner extends Thread
      {
          private Runnable m_runnable;
          
          protected Runner( Runnable runnable, String name )
          {
              super( name );
              m_runnable = runnable;
          }
          
          public void run()
          {
              try
              {
                  // Need all threads to wait until all the others are ready.
                  synchronized(m_semaphore)
                  {
                      m_startedCount++;
                      getLogger().debug( "Started " + m_startedCount + " test threads." );
                      if ( m_startedCount >= m_threads.length )
                      {
                          m_semaphore.notifyAll();
                      }
                      while ( !m_latched )
                      {
                          try
                          {
                              m_semaphore.wait();
                          }
                          catch ( InterruptedException e ) {}
                      }
                  }
                  
                  // Run the runnable
                  try
                  {
                      m_runnable.run();
                  }
                  catch (Throwable t)
                  {
                      synchronized(m_semaphore)
                      {
                          getLogger().error( "Error in " + Thread.currentThread().getName(), t );
                          if ( m_exception != null )
                          {
                              m_exception = t;
                          }
                      }
                  }
              }
              finally
              {
                  // Say that we are done
                  synchronized(m_semaphore)
                  {
                      m_completedCount++;
                      getLogger().debug( m_completedCount + " test threads completed." );
                      m_semaphore.notifyAll();
                  }
              }
          }
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>