FileDocCategorySizeDatePackage
RunnableQueue.javaAPI DocphoneME MR2 API (J2ME)26912Wed May 02 18:00:34 BST 2007com.sun.perseus.util

RunnableQueue

public final class RunnableQueue extends Object implements Runnable
This class represents an object which queues Runnable objects for invocation in a single thread. This class is derived from work done in the Batik project but was seriously modified and extended.
version
$Id: RunnableQueue.java,v 1.5 2006/04/21 06:35:50 st125089 Exp $

Fields Summary
public static final String
RUNNING
The queue is in the processes of running tasks.
public static final String
SUSPENDING
The queue may still be running tasks but as soon as possible will go to SUSPENDED state.
public static final String
SUSPENDED
The queue is no longer running any tasks and will not run any tasks until resumeExecution is called.
public static final String
TERMINATED
This queue has been interrupted
protected static RunnableQueue
defaultQueue
The default RunnableQueue instance.
protected String
state
The Suspension state of this thread.
protected Object
stateLock
Object to synchronize/wait/notify for suspension issues.
protected Scheduler
scheduler
The Scheduler which can run Runnables at a fixed rate.
protected DoublyLinkedList
list
The Runnable objects list, also used as synchoronization point for pushing/poping runables.
protected RunnableQueueHandler
queueHandler
The object which handles RunnableQueue events.
protected Thread
runnableQueueThread
The current thread.
private static int
threadCount
Used for the RunnableQueue's thread names.
Constructors Summary
private RunnableQueue()
All RunnableQueue instances should be created through the createRunnableQueue method.

see
#createRunnableQueue


                      
      
    
Methods Summary
public static com.sun.perseus.util.RunnableQueuecreateRunnableQueue(com.sun.perseus.util.RunnableQueue$RunnableQueueHandler queueHandler)
Creates a new RunnableQueue started in a new thread.

param
queueHandler the RunnableQueueHandler which will be notified of the RunnableQueue's activity. May be null.
return
a RunnableQueue which is garanteed to have entered its run() method.

        RunnableQueue result = new RunnableQueue();

        // Configure the RunHandler
        if (queueHandler == null) {
            queueHandler = new VoidQueueHandler();
        }
        result.queueHandler = queueHandler;

        // Start the thread. We use the RunnableQueue instance as
        // a lock to synchronize between this method and the
        // run method (called from the RunnableQueue thread)
        synchronized (result) {
            Thread t = new Thread(result, "RunnableQueue-" + threadCount++);
            ThreadSupport.setDaemon(t, true);
            t.setPriority(Thread.MIN_PRIORITY);
            t.start();
            while (result.getThread() == null) {
                try {
                    // See the run() method. It calls notify on 
                    // the RunnableQueue instance to notify us
                    // that the thread has started executing
                    result.wait();
                } catch (InterruptedException ie) {
                }
            }
        }

        // Wait until we get into suspended state. State changes
        // are synchronized with the stateLock lock.
        synchronized (result.stateLock) {
            try {
                while (result.state != SUSPENDED) {
                    result.stateLock.wait();
                }
            } catch (InterruptedException ie) {
            }
        }

        return result;
    
public voidempty()
Removes all pending Runnables.

        synchronized (list) {
            list.empty();
        }
    
public static com.sun.perseus.util.RunnableQueuegetDefault()
Returns the default RunnableQueue instance. This is what should be used in most circumstances. In particular, all document instances which need to process in a seperate thread should share this default RunnableQueue.

return
the default RunnableQueue instance.

        if (defaultQueue != null) {
            return defaultQueue;
        }

        defaultQueue = RunnableQueue.createRunnableQueue(new VoidQueueHandler());
        defaultQueue.resumeExecution();

        return defaultQueue;
    
public java.lang.RunnablegetNextPending()

return
the next pending runnable

        synchronized (list) {
            if (list.getSize() == 0) {
                return null;
            } else {
                return ((Link) list.getHead()).runnable;
            }
        }
    
public java.lang.StringgetQueueState()

return
this queue's state, one of RUNNING, SUSPENDING, SUSPENDED or TERMINATED

 
        synchronized (stateLock) {
            return state; 
        }
    
public intgetSize()

return
the number of pending runnables

        synchronized (list) {
            return list.getSize();
        }
    
public java.lang.ThreadgetThread()
Returns the thread in which the RunnableQueue is currently running.

return
null if the RunnableQueue has not entered his run() method.

        return runnableQueueThread;
    
public voidinvokeAndWait(java.lang.Runnable r, com.sun.perseus.util.RunnableQueue$RunnableHandler runHandler)
Waits until the given Runnable's run() has returned. Note: invokeAndWait() must not be called from the current thread (for example from the run() method of the argument).

param
r the Runnable to put at the end of the execution list.
param
runHandler the RunnableHandler to notify once the Runnable has finished executing. Should not be null.
throws
IllegalStateException if getThread() is null or if the thread returned by getThread() is the current one.
throws
InterruptedException if the thread is interrupted while waiting for the input Runnable to complete its execution.


        if (runnableQueueThread == null) {
            throw new IllegalStateException
                ("RunnableQueue not started or has exited");
        }
        if (runnableQueueThread == Thread.currentThread()) {
            throw new IllegalStateException
                ("Cannot be called from the RunnableQueue thread");
        }

        LockableLink l = new LockableLink(r, runHandler);
        synchronized (list) {
            list.push(l);
            list.notify();
        }
        l.lock();
    
public voidinvokeLater(java.lang.Runnable r, com.sun.perseus.util.RunnableQueue$RunnableHandler runHandler)
Schedules the given Runnable object for a later invocation, and returns. An exception is thrown if the RunnableQueue was not started.

param
r the Runnable to put at the end of the execution list.
param
runHandler the RunnableHandler to notify once the Runnable has finished executing. Should not be null.
throws
IllegalStateException if getThread() is null.

        if (runnableQueueThread == null) {
            throw new IllegalStateException
                ("RunnableQueue not started or has exited");
        }

        synchronized (list) {
            list.push(new Link(r, runHandler));
            list.notify();
        }
    
public voidpreemptAndWait(java.lang.Runnable r, com.sun.perseus.util.RunnableQueue$RunnableHandler runHandler)
Waits until the given Runnable's run() has returned. The given runnable preempts any runnable that is not currently executing (ie the next runnable started will be the one given). Note: preemptAndWait() must not be called from the current thread (for example from the run() method of the argument).

param
r the Runnable to execute
param
runHandler the RunnableHandler to notify once the Runnable has finished executing. Should not be null.
throws
IllegalStateException if getThread() is null or if the thread returned by getThread() is the current one.
throws
InterruptedException if the thread is interrupted while waiting for the completion of the input Runnable to complete execution.


        if (runnableQueueThread == null) {
            throw new IllegalStateException
                ("RunnableQueue not started or has exited");
        }
        if (runnableQueueThread == Thread.currentThread()) {
            throw new IllegalStateException
                ("Cannot be called from the RunnableQueue thread");
        }

        LockableLink l = new LockableLink(r, runHandler);
        synchronized (list) {
            list.unpop(l);
            list.notify();
        }
        l.lock();
    
public voidpreemptLater(java.lang.Runnable r, com.sun.perseus.util.RunnableQueue$RunnableHandler runHandler)
Schedules the given Runnable object for a later invocation, and returns. The given runnable preempts any runnable that is not currently executing (ie the next runnable started will be the one given). An exception is thrown if the RunnableQueue was not started.

param
r the Runnable to put at the front of the execution list.
param
runHandler the RunnableHandler to notify once the Runnable has finished executing. Should not be null.
throws
IllegalStateException if getThread() is null.

        if (runnableQueueThread == null) {
            throw new IllegalStateException
                ("RunnableQueue not started or has exited");
        }
        synchronized (list) {
            list.unpop(new Link(r, runHandler));
            list.notify();
        }
    
public voidresumeExecution()
Resumes the execution of this queue.

throws
IllegalStateException if getThread() is null.

        if (runnableQueueThread == null) {
            throw new IllegalStateException
                ("RunnableQueue not started or has exited");
        }

        synchronized (stateLock) {
            if (state != RUNNING) {
                state = RUNNING;
                stateLock.notifyAll(); // wake it up.
                try {
                    // Wait until we have really resumed
                    stateLock.wait(); 
                } catch (InterruptedException ie) {
                    // The calling thread was interrupted
                }
            }
        }
    
public voidrun()
Runs this queue. Implements the Runnable interface.

        //
        // This object is used as a lock to synchronize on the
        // queue's thread execution start. 
        //
        synchronized (this) {
            runnableQueueThread = Thread.currentThread();
            // Wake the create method so it knows we are in
            // our run and ready to go.
            notify();
        }

        Link l = null;
        Runnable rable = null, sRable;
        long t = 0;
        long wait = 0;

        try {
            while (!ThreadSupport.isInterrupted(Thread.currentThread())) {
                // Mutex for suspending/resuming work.
                synchronized (stateLock) {
                    if (state != RUNNING) {
                        state = SUSPENDED;

                        // notify suspendExecution in case it is
                        // waiting til we shut down.
                        stateLock.notifyAll();

                        queueHandler.executionSuspended(this);

                        while (state != RUNNING) {
                            state = SUSPENDED;
                            // Wait until resumeExecution called.
                            stateLock.wait(); 
                        }

                        // notify resumeExecution as it waits until 
                        // execution are really resumed
                        stateLock.notifyAll();
                        queueHandler.executionResumed(this);
                    }
                }

                // First, run the Scheduler to take care of all the pending
                // fixed rate Runnables.
                t = System.currentTimeMillis();
                scheduler.run(t);

                synchronized (list) {
                    l = (Link) list.pop();
                    if (l == null) {
                        // Wait until the next scheduled runnable
                        wait = scheduler.nextRun(System.currentTimeMillis());

                        if (wait == 0) {
                            wait = 1;
                        }

                        if (state == SUSPENDING) {
                            continue;
                        }

                        if (wait > 0) {
                            list.wait(wait);
                        } else {
                            // There is no scheduled runnable at this point.
                            list.wait();
                        }

                        continue; // start loop over again...
                    }
                    
                    rable = l.runnable;
                }

                try {
                    rable.run();
                } catch (Exception e) {
                    // Might be nice to notify someone directly.
                    // But this is more or less what Swing does.
                    e.printStackTrace();
                }
                
                if (l.runHandler != null) {
                    l.runHandler.runnableInvoked(this, rable);
                }

                l.unlock();
                rable = null;
            }
        } catch (InterruptedException e) {
            if (this == defaultQueue) {
                defaultQueue = null;
            }
            e.printStackTrace();
        } finally {
            if (this == defaultQueue) {
                defaultQueue = null;
            }
            System.err.println(">>>>>>>>>>>>>> RunnableQueue terminating");
            synchronized (this) {
                runnableQueueThread = null;
            }
            synchronized (stateLock) {
                state = TERMINATED;
                stateLock.notifyAll();
            }
        }
    
public voidsafeInvokeAndWait(java.lang.Runnable r, com.sun.perseus.util.RunnableQueue$RunnableHandler runHandler)
Waits until the given Runnable's run() has returned. Note: safeInvokeAndWait() may be called from any thread. This method checks if this thread is the update thread, in which case the Runnable is invoked directly. Otherwise, it delegates to the invokeAndWait method.

param
r the Runnable to put at the end of the execution list. Should not be null.
param
runHandler the RunnableHandler to notify once the Runnable has finished executing. Should not be null.
throws
IllegalStateException if getThread() is null or if the thread returned by getThread() is the current one.

        if (runnableQueueThread == Thread.currentThread()) {
            r.run();
            runHandler.runnableInvoked(this, r);
        }

        try {
            invokeAndWait(r, runHandler);
        } catch (InterruptedException ie) {
            // We are in a bad state because the thread was interrupted while 
            // waiting for the runnable to complete.
            throw new IllegalStateException();
        }
    
public voidscheduleAtFixedRate(java.lang.Runnable r, com.sun.perseus.util.RunnableQueue$RunnableHandler runHandler, long interval)
Schedules the input Runnable at the requested fixed rate. The RunnableQueue offers a 'best' effort service meaning that it will schedule the Runnable as soon as possible so that the time between the begining of two consecutive runs of the Runnable is as close as possible to the requested rate. Note that a too high rate may cause the rest of the Runnable in the RunnableQueue to be starved and never get executed.

param
r the Runnable to schedule at a regular interval. If null, there won't be any Runnable scheduled and if there was a current one, it won't be executed any more.
param
runHandler the RunnableHandler to notify once the Runnable has finished executing. Should not be null.
param
interval the minimum interval between to consecutive executions of the input Runnable. The value is in milliseconds.
throws
IllegalArgumentException If this parameter is zero or less, and r is not null.

        scheduler.add(r, interval, runHandler);

        // In case the queue is running and waiting for an item in the
        // list, notify the list so that we can get the animation loop
        // going. See the run() method.
        synchronized (list) {
            list.notify();
        }
    
public voidsuspendExecution(boolean waitTillSuspended)
Suspends the execution of this queue after the current runnable completes.

param
waitTillSuspended if true this method will not return until the queue has suspended (no runnable in progress or about to be in progress). If resumeExecution is called while waiting will simply return (this really indicates a race condition in your code). This may return before an associated RunHandler is notified.
throws
IllegalStateException if getThread() is null.

        if (runnableQueueThread == null) {
            throw new IllegalStateException
                ("RunnableQueue not started or has exited");
        }
        synchronized (stateLock) {
            if (state == SUSPENDED) {
                // already suspended...
                return;
            }

            if (state == RUNNING) {
                state = SUSPENDING;
                synchronized (list) {
                    // Wake up run thread if it is waiting for jobs,
                    // so we go into the suspended case (notifying
                    // run-handler etc...)
                    list.notify();
                }
            }

            if (waitTillSuspended) {
                try {
                    stateLock.wait();
                } catch (InterruptedException ie) { }
            }
        }
    
public voidunschedule(java.lang.Runnable r)
Removes the input Runnable from the list of Runnables scheduled at a fixed rate. If the Runnable is not currently scheduled at a fixed rate, then this method does nothing. If this Runnable was scheduled multiple times with this RunnableQueue, then all instances are removed.

param
r the Runnable that should no longer be scheduled at a fixed rate.
see
#scheduleAtFixedRate

        scheduler.remove(r);