FileDocCategorySizeDatePackage
RunnableBase.javaAPI DocGlassfish v2 API15632Fri May 04 22:31:06 BST 2007com.sun.appserv.management.util.misc

RunnableBase

public abstract class RunnableBase extends Object implements Runnable
INTERNAL USE ONLY -- DO NOT USE Base class (can be used directly) for running small, short lived tasks. An ExecutorService is used for efficiency, and excess threads are discarded quickly.

Includes convenience routines for submitting tasks, determining result status.

Example (inline usage)

final RunnableBase myTask = new RunnableBase( "Compute PI" ) {
public void run() {
final double pi = 3.141926386; // cheater method
}
};
myTask.submit();
...
// wait for compute task
myTask.waitDoneThrow(); // or waitDone() followed by myTask.getThrowable()

NOTE: An ExecutorService is used with a thread pool. Inheritable thread local variables will not be inherited.

Fields Summary
private volatile Throwable
mThrowable
a Throwable if anything was thrown from the run loop
private volatile CountDownLatch
mLatch
means to block client threads until done
private final String
mName
optional name of the task
private volatile boolean
mUseRandomSleep
debugging: whether to sleep a random amount. See {@link #setUseRandomSleep}
private final T
mData
private volatile long
mSubmitNanos
private volatile long
mRunStartNanos
private volatile long
mRunDoneNanos
private final com.sun.appserv.management.helper.AMXDebugHelper
mDebug
private static final AtomicInteger
mThreadsRunning
private static final ExecutorService
_DefaultExecutorService
private static final long
MAX_RANDOM_SLEEP_MILLIS
private static final com.sun.appserv.management.util.misc.Timings
TIMINGS
Constructors Summary
protected RunnableBase(String name)

        this( name, null );
    
protected RunnableBase()

        this( null );
    
protected RunnableBase(String name, T data)
Create a new task.

param
name use-readable name of the task
param
data optional arbitrary data (see {@link #getData})

        mDebug  = new AMXDebugHelper( "RunnableBase-" + name );
        mDebug.setEchoToStdOut( true );
        
        mName       = name == null ? (this.getClass().getName() + ".<no_name>") : name ;
        mData       = data;
        mThrowable  = null;
        mSubmitNanos    = 0;
        mRunStartNanos    = 0;
        mRunDoneNanos    = 0;
        
        
        mUseRandomSleep = false;    // good for debugging
        
        mLatch   = null;
    
Methods Summary
private void_submit(com.sun.appserv.management.util.misc.RunnableBase$HowToRun howToRun)

        if ( howToRun != HowToRun.RUN_IN_CURRENT_THREAD && howToRun != HowToRun.RUN_IN_SEPARATE_THREAD )
        {
            throw new IllegalArgumentException();
        }
        
        if ( mLatch != null )
        {
            // already in progress
            throw new IllegalStateException();
        }
        
        mSubmitNanos    = System.nanoTime();
        
        if ( howToRun == HowToRun.RUN_IN_CURRENT_THREAD )
        {
            runSync();
        }
        else
        {
            mLatch  = new CountDownLatch(1);
            
            runInSeparateThread( this );
        }
    
private static java.util.concurrent.ExecutorServicecreateExecutorService()

        // Testing at startup shows that a thread pool equal in size to the number
        // of processors offers the best performance.  However, this can 'hang'
        // services that expect their threads to run once submitted.
        return Executors.newCachedThreadPool();
    
private voiddebug(java.lang.Object args)

    
         
       
    
        if ( mDebug.getDebug() )
        {
            mDebug.println( args );
        }
    
protected abstract voiddoRun()

public final TgetData()

 return mData; 
public static java.util.concurrent.ExecutorServicegetDefaultExecutorService()

    
          
    
    
        return _DefaultExecutorService;
    
protected java.util.concurrent.ExecutorServicegetExecutorService()
Subclasses may override the choice of ExecutorService

        return getDefaultExecutorService();
    
public java.lang.StringgetName()

        return (mName == null || mName.length() == 0) ? this.getClass().getName() : mName;
    
public longgetNanosFromRunStart()

return
the number of nanoseconds to execute the task from the time it actually entered the {@link #run} method.

        return mRunDoneNanos - mRunStartNanos;
    
public longgetNanosFromSubmit()

return
the number of nanoseconds to execute the task from the time it was submitted

        return mRunDoneNanos - mSubmitNanos;
    
public com.sun.appserv.management.util.misc.RunnableBase$HowToRungetRecommendedSubmitType()
Taking into account single vs multi-core, the number of RunnableBase currently running, return a recommended HowToRun.

Callers that know significant I/O is involved should usually submit using HowToRun.RUN_IN_SEPARATE_THREAD, even on single processor machines.

Callers with long-running tasks should generally not call this method; it's best used with numbers of short-running tasks.

A subclass that knows it performs I/O might override this method to usually or always return HowToRun.RUN_IN_SEPARATE_THREAD.

        final int       numProcessors = Runtime.getRuntime().availableProcessors();
        final boolean   singleCore = numProcessors == 1;
        
        HowToRun    howToRun    = HowToRun.RUN_IN_CURRENT_THREAD;
        
        if ( singleCore )
        {
            // try to keep it to just one thread; there could be some I/O
            howToRun    = mThreadsRunning.intValue()  <= 1 ?
                            HowToRun.RUN_IN_SEPARATE_THREAD : HowToRun.RUN_IN_CURRENT_THREAD;
        }
        else
        {
            final int CUTOFF = numProcessors * 2;
            if ( mThreadsRunning.intValue() <= CUTOFF )
            {
                howToRun    = HowToRun.RUN_IN_SEPARATE_THREAD;
            }
            else
            {
                // all cores are busy (though this might be stale as soon as we checked).
                howToRun = HowToRun.RUN_IN_CURRENT_THREAD;
            }
        }
        return howToRun;
    
public longgetRunLatency()

return
the number of nanoseconds between task-submittal and the actual execution start

        return mRunStartNanos - mSubmitNanos;
    
public static com.sun.appserv.management.util.misc.TimingsgetTimings()

          
    
    
        return TIMINGS;
    
protected java.lang.ThrowablelaunderThrowable(java.lang.Throwable t)
A subclass may transform the thrown exception (if any) into a more appropriate or expected kind.

        return t;
    
public final voidrun()
May be called synchronously or via another thread {@link #submit}. See {@link #waitDone} and {@link #waitDoneThrow} and {@link #getThrowable}.

        final int numRunning = mThreadsRunning.incrementAndGet();
       // debug("Submitted ", getName(), ", #of threads = ", numRunning  );
            
        try {
            runSync();
        }
        finally {
            mThreadsRunning.decrementAndGet();
        }
    
protected voidrunInSeparateThread(java.lang.Runnable r)
Run in a separate thread. Calls getExecutorService().submit(r). Subclasses may override if desired.

        getExecutorService().submit( r );
    
private final voidrunSync()

        mRunStartNanos    = System.nanoTime();
        
        if ( mUseRandomSleep )
        {
            final long sleepMillis = (System.currentTimeMillis() >> 4)  % MAX_RANDOM_SLEEP_MILLIS;
            debug( "Random sleep for: " + sleepMillis + "ms" );
            sleepMillis( sleepMillis );
        }
        try
        {
            doRun();
        }
        catch( Throwable t )
        {
            mThrowable  = t;
        }
        finally
        {
            mRunDoneNanos    = System.nanoTime();
            //debug( toString() );
            if ( mLatch != null )   // could be null if RUN_IN_CURRENT_THREAD
            {
                mLatch.countDown();
                mLatch  = null; // it only counts down to 1, so forget about it
            }
            // do this after we release the latch
            final String msg = "RunnableBase-" + StringUtil.quote(getName());
            final long runTime    = getNanosFromSubmit();
            getTimings().add( msg, runTime);
            //debug( "TIME TO ADD TIMING: " + (System.nanoTime() - start ) );
        }
    
public voidsetUseRandomSleep(boolean useRandom)
Good for debugging timing issues; a task will insert an artificial delay by a random amount.

    
                                       
          
        
    
        mUseRandomSleep = useRandom;
    
protected static voidsleepMillis(long millis)

        try
        {
            Thread.sleep( millis );
        }
        catch( InterruptedException e )
        {
        }
    
public voidsubmit()
Calls submit( RUN_IN_SEPARATE_THREAD ).

        _submit( getRecommendedSubmitType() );
    
public voidsubmit(com.sun.appserv.management.util.misc.RunnableBase$HowToRun howToRun)
Submit the task for execution with {@link #submit()}. If 'waitTillDone' is true, then this method won't return until the task has finished. This method is useful as a transition method in the course of converting from serialized execution to threaded execution, allowing a simple boolean switch to make the change in behavior.

The task is still executed in its own thread, so as to produce the same runtime environment that would be used for asynchronous execution (eg thread-local variables).

param
waitTillDone if true, the method executes synchronously

        _submit( howToRun );
    
public java.lang.StringtoString()

        final String delim = ", ";
        
        final boolean started   = mSubmitNanos != 0;
        final boolean done      = mRunDoneNanos != 0;
        final long runTimeNanos      = started ?
            (done ? (mRunDoneNanos - mRunStartNanos) : System.nanoTime() - mRunStartNanos) : 0;
        final String throwable = mThrowable == null ? "" : mThrowable.toString();
        
        final String runTimeString = StringUtil.getTimingString( runTimeNanos );
        
        return "Runnable \"" + this.getClass().getName() + "\"" + delim + "name = " + getName() +
            delim + "started=" + started + delim + "done=" + done +
            delim + "run-time=" + runTimeString + delim + throwable;
    
public final java.lang.ThrowablewaitDone()
Block until the task has finished, and return any Throwable (hopefully null).

return
the Throwable that was thrown (if any), otherwise null

        // if mLatch is null, it was run synchronously, or has already finished (or never started)
        // use temp, avoid race condition between null check and usage should mLatch go null
        // after check for null
        final CountDownLatch latch  = mLatch;
        if ( latch != null )
        {
            try
            {
                latch.await();
            }
            catch( final InterruptedException intr )
            {
               throw new RuntimeException( intr );
            }
        }
        return mThrowable;
    
public final voidwaitDoneThrow()
Block until the task has finished. If a Throwable was thrown, then this method will rethrow it, or a RuntimeException.

        final Throwable t   = waitDone();
        if ( t != null )
        {
            if ( t instanceof RuntimeException )
            {
                throw (RuntimeException)t;
            }
            else if ( t instanceof Error )
            {
                throw (Error)t;
            }
            else
            {
                throw new RuntimeException( t );
            }
        }