net.groboclown.gui.paintpipe.v1
Class PainterThread

java.lang.Object
  |
  +--net.groboclown.gui.paintpipe.v1.PainterThread
Direct Known Subclasses:
OutputPainter, RefreshPainter, TimerThread

public abstract class PainterThread
extends Object

Describes an outline for how threads dedicated to painting should handle and maintain themselves. The main processing of data is maintained in the paint() routine.

There is a logic to how the processing of painting flows. It forces upon the developer to conform a set of guidelines, but hopefully it will only add focus to the application, as opposed to weighing it down.

The routine paint() handles all processing for creating images, and directing them to the appropriate source. Herein, an application can create multiple images, and wait on Queues to produce data. The routine is prohibited from catching InterruptedException events, and instead must let them propigate to the next level. This is required for proper clean-up of the thread upon kill events. If the programmer needs to clean-up any allocated resources at kill time, use the cleanup() routine to handle this. Likewise, if the paint() process needs specific setup for its execution, use the initialize() routine to handle this.

The class handles the PaintFinishedEvent and KillPaintingEvent processing, without the programmer having to add any extra processing.

Added functionality has been added to test for metrics. If you're not concerned about this, then turn METRICS to false.

Only one thread should have control over the PainterThread object. Otherwise, it is not guaranteed to be thread-safe. In other words, the thread setup and shutdown routines are not thread-safe for speed reasons, while the internal thread operations are thread-safe. If need be, this can be modified in the future by synchronizing all thread modification methods: start(), suspend(), resume(), stop(), stopInterrupt(), suspendInterrupt(), join(), stopJoin(), stopInterruptJoin(), and any others I may have missed.

The thread automatically cleans itself up when it terminates. This takes place on the thread's own time.

Dependent subclasses can check their state to see if their paint method has been interrupted by calling isInterrupted(). This clears the thread's interrupted status flag, and generates an InterruptedException. This is useful in a paint method if there are stopping points between thick processing where a check needs to be made if it was interrupted.

Events are processed as so:

PaintFinishedEvent
this event is notified by the call to paintFinished(net.groboclown.gui.paintpipe.v1.PaintFinishedEvent). It informs the thread that it is ok to paint the next frame. One of these events is needed to get the painting going.
KillPaintingEvent
this event is notified by the call to killPainting(net.groboclown.gui.paintpipe.v1.KillPaintingEvent). It stops the thread by a call to stopInterruptJoin().

Version:
1.0
Author:
Matt Albrecht

Inner Class Summary
(package private)  class PainterThread.PaintRunnable
          This inner class handles the Runnable calls.
 
Field Summary
private  int frameNumber
           
private  ThreadGroup group
           
private  boolean isPainting
           
static boolean METRICS
          True if metrics gathering is enabled, or False if not.
private  Vector metricsListeners
           
private  String name
           
private  int numberPaintAttempts
           
private  int numberWaitForEvent
           
private  Thread painter
           
private  int priority
           
private static int RUN_NEVER_STARTED
           
private static int RUN_PAUSED
           
private static int RUN_REQUESTED_PAUSE
           
private static int RUN_REQUESTED_STOP
           
private static int RUN_RUNNING
           
private static int RUN_STOPPED
           
private  int runState
           
private  int sentPaintFinishedEvent
           
private  long startTimeMillis
           
private  Object syncThis
           
private  long totalPaintTimeMillis
           
private  long totalWaitForEventTimeMillis
           
 
Constructor Summary
PainterThread()
          Currently, the constructor doesn't do anything, except allocate the bare-minimum data.
PainterThread(int priority)
          Sets up the thread with a given priority.
PainterThread(String name)
          Sets up the thread with the given name.
PainterThread(String name, int priority)
          Sets up a thread with the given name and priority.
PainterThread(String name, ThreadGroup tg)
          Sets up the thread with the given name and thread group.
PainterThread(String name, ThreadGroup tg, int priority)
          Sets up the thread with the given name, thread group, and priority.
PainterThread(ThreadGroup tg)
          Sets up the thread with the given thread group.
PainterThread(ThreadGroup tg, int priority)
          Sets up a thread with the given thread group and priority.
 
Method Summary
 void addMetricsListener(MetricsListener ml)
          Adds a metrics listener.
private  void canPause()
           
private  void canStop()
           
protected  void cleanup()
          This enables a subclass to cleanup after a painter thread has stopped.
private  void fireMetricsPaint(long duration)
           
private  void fireMetricsWait(long duration)
           
 int getFrameNumber()
          Retrieves the current frame number the thread thinks its on.
 int getNumberPaintCalls()
          Retrieves the total number of times the paint() method was entered.
 int getNumberWaitForEventTimes()
          Returns the number of times the painting thread has waited for a PaintFinishedEvent to be generated.
 long getStartTimeMillis()
          Reports the time at which the thread's run method was first called, as reported by java.lang.System#currentTimeMillis().
 long getTotalPaintTimeMillis()
          Retrieves the total amount of time spent in milliseconds inside the paint() method.
 long getTotalWaitForEventTimeMillis()
          Retrieves the total time spent in milliseconds waiting for a PaintFinishedEvent to be generated.
protected  void initialize()
          This enables a subclass to setup a painter before the thread starts.
 boolean isAlive()
          Tests if the thread is currently stopped or is running.
protected  void isInterrupted()
          Called inside a paint() method to check if an interrupt was generated.
 boolean isRunning()
          Tests if the thread is currently running.
 boolean isStopImpending()
          Tests if a request has been submitted to stop the thread, or if the thread has stopped, or if the thread has never run.
private  void isStopping()
           
 boolean isSuspended()
          Responds true if the thread is paused, or has been requested to be paused.
 boolean isWaitingForEvent()
          Checks if the painter is waiting on a PaintFinishedEvent.
 void join()
          Joins with the thread, waiting for it to stop.
 void killPainting(KillPaintingEvent kpe)
          This causes the current thread to stop as quickly as possible.
protected abstract  void paint()
          The programmer must implement all paint-related activities here.
 void paintFinished(PaintFinishedEvent pfe)
          Processes a paint finished event.
 void removeMetricsListener(MetricsListener ml)
          Removes a registered metrics listener.
 void resume()
          Attempts to unpause the thread.
 void start()
          Starts the thread going.
 void stop()
          Tells the thread to stop.
 void stopInterrupt()
          Tells the thread to stop.
 void stopInterruptJoin()
          Joins with the thread, waiting for it to stop.
 void stopJoin()
          Joins with the thread, waiting for it to stop.
 void suspend()
          Tells the thread to pause.
 void suspendInterrupt()
          Tells the thread to pause.
 
Methods inherited from class java.lang.Object
, clone, equals, finalize, getClass, hashCode, notify, notifyAll, registerNatives, toString, wait, wait, wait
 

Field Detail

METRICS

public static final boolean METRICS
True if metrics gathering is enabled, or False if not. Metrics are a finicky thing. We need to make sure that metrics gathering only gathers the minimal data, while metrics reporting allows all sorts of combinations and computations. This is important since metrics gathering takes up painting time, while metrics reporting takes up the reporter's time.

metricsListeners

private Vector metricsListeners

frameNumber

private int frameNumber

syncThis

private Object syncThis

painter

private Thread painter

priority

private int priority

group

private ThreadGroup group

name

private String name

runState

private int runState

RUN_NEVER_STARTED

private static final int RUN_NEVER_STARTED

RUN_RUNNING

private static final int RUN_RUNNING

RUN_PAUSED

private static final int RUN_PAUSED

RUN_STOPPED

private static final int RUN_STOPPED

RUN_REQUESTED_PAUSE

private static final int RUN_REQUESTED_PAUSE

RUN_REQUESTED_STOP

private static final int RUN_REQUESTED_STOP

sentPaintFinishedEvent

private int sentPaintFinishedEvent

isPainting

private boolean isPainting

startTimeMillis

private long startTimeMillis

totalPaintTimeMillis

private long totalPaintTimeMillis

numberPaintAttempts

private int numberPaintAttempts

totalWaitForEventTimeMillis

private long totalWaitForEventTimeMillis

numberWaitForEvent

private int numberWaitForEvent
Constructor Detail

PainterThread

public PainterThread()
Currently, the constructor doesn't do anything, except allocate the bare-minimum data.

PainterThread

public PainterThread(int priority)
Sets up the thread with a given priority.

PainterThread

public PainterThread(ThreadGroup tg)
Sets up the thread with the given thread group.

PainterThread

public PainterThread(String name)
Sets up the thread with the given name.

PainterThread

public PainterThread(String name,
                     ThreadGroup tg)
Sets up the thread with the given name and thread group.

PainterThread

public PainterThread(ThreadGroup tg,
                     int priority)
Sets up a thread with the given thread group and priority.

PainterThread

public PainterThread(String name,
                     int priority)
Sets up a thread with the given name and priority.

PainterThread

public PainterThread(String name,
                     ThreadGroup tg,
                     int priority)
Sets up the thread with the given name, thread group, and priority.
Method Detail

initialize

protected void initialize()
This enables a subclass to setup a painter before the thread starts. It, by default, performs no action, so the super class method need not be invoked.

cleanup

protected void cleanup()
This enables a subclass to cleanup after a painter thread has stopped. It is guaranteed to be executed in a safe way, such that the thread is guaranteed to be stopped. This guarantee is not in place if it is executed outside this class, so it is suggested that it not be explicitly called by any other class.

By default, this method doesn't do anything, so the super class's method need not be invoked.


paint

protected abstract void paint()
                       throws InterruptedException
The programmer must implement all paint-related activities here. it is executed within a thread. All InterruptedException events must be allowed to propigate up without being caught.
Throws:
InterruptedException - must be allowed to propigate without being caught. It is thrown when the thread has been interrupted during some kind of Thread.sleep(long) or Object.wait() call.

addMetricsListener

public void addMetricsListener(MetricsListener ml)
Adds a metrics listener. If metrics are disabled, listeners will never be called.

removeMetricsListener

public void removeMetricsListener(MetricsListener ml)
Removes a registered metrics listener.

getFrameNumber

public int getFrameNumber()
Retrieves the current frame number the thread thinks its on.

isSuspended

public boolean isSuspended()
Responds true if the thread is paused, or has been requested to be paused.

isAlive

public boolean isAlive()
Tests if the thread is currently stopped or is running.
Returns:
true if the thread is running or paused, or false if it has stopped or hasn't started running. This is not affected by requests to stop the thread. It only returns the actual state of the thread.

isStopImpending

public boolean isStopImpending()
Tests if a request has been submitted to stop the thread, or if the thread has stopped, or if the thread has never run.

isRunning

public boolean isRunning()
Tests if the thread is currently running.
Returns:
true if the thread is running. Otherwise, false if the thread is stopped, has never run, is or is paused. It is unaffected by requests to stop or pause the thread.

isWaitingForEvent

public boolean isWaitingForEvent()
Checks if the painter is waiting on a PaintFinishedEvent. Returns false if it is currently painting an image.

getStartTimeMillis

public long getStartTimeMillis()
Reports the time at which the thread's run method was first called, as reported by java.lang.System#currentTimeMillis().

getTotalPaintTimeMillis

public long getTotalPaintTimeMillis()
Retrieves the total amount of time spent in milliseconds inside the paint() method.

getNumberPaintCalls

public int getNumberPaintCalls()
Retrieves the total number of times the paint() method was entered.

getTotalWaitForEventTimeMillis

public long getTotalWaitForEventTimeMillis()
Retrieves the total time spent in milliseconds waiting for a PaintFinishedEvent to be generated.

getNumberWaitForEventTimes

public int getNumberWaitForEventTimes()
Returns the number of times the painting thread has waited for a PaintFinishedEvent to be generated.

start

public void start()
           throws IllegalStateException
Starts the thread going. This can only be done if the thread has been stopped or never started.
Throws:
IllegalStateException - thrown if the thread is currently running.

suspend

public void suspend()
             throws IllegalStateException
Tells the thread to pause. This does not cancel the current paint method, if it's being executed.
Throws:
IllegalStateException - thrown if the state of the thread is in such a way that it can't be paused. Situations where this arises from is where the thread is already paused, or it has stopped and can't be paused, or the thread hasn't started yet.

stop

public void stop()
          throws IllegalStateException
Tells the thread to stop. This does not cancel the current paint method, if it's being executed. If the thread is paused, it is unpaused.

Note that this does not clean up the thread. To do that, a join() method must be called, or the thread needs to be restarted.

Throws:
IllegalStateException - thrown if the state of the thread is in such a way that it can't be stopped. Situations where this arises from is where the thread is already stopped, or the thread hasn't started yet.

resume

public void resume()
            throws IllegalStateException
Attempts to unpause the thread. If the thread is not currently paused, an IllegalStateException will be thrown.
Throws:
IllegalStateException - thrown if the thread is not currently paused.

suspendInterrupt

public void suspendInterrupt()
                      throws IllegalStateException
Tells the thread to pause. This cancels the current paint method, if it's being executed.
Throws:
IllegalStateException - thrown if the state of the thread is in such a way that it can't be paused. Situations where this arises from is where the thread is already paused, or it has stopped and can't be paused, or the thread hasn't started yet.

stopInterrupt

public void stopInterrupt()
                   throws IllegalStateException
Tells the thread to stop. This cancels the current paint method, if it's being executed. If the thread is paused, it is unpaused.

Note that this does not clean up the thread. To do that, a join() method must be called, or the thread needs to be restarted.

Throws:
IllegalStateException - thrown if the state of the thread is in such a way that it can't be stopped. Situations where this arises from is where the thread is already stopped, or the thread hasn't started yet.

join

public void join()
          throws IllegalStateException,
                 InterruptedException
Joins with the thread, waiting for it to stop. This cleans up the thread safely.
Throws:
IllegalStateException - thrown if the thread is not slated to be stopped
InterruptedException - propigated Thread.join() method exception.

stopJoin

public void stopJoin()
              throws IllegalStateException,
                     InterruptedException
Joins with the thread, waiting for it to stop. If the thread hasn't stopped yet, it performs a soft stop, waiting for the paint() method to quit.

This cleans up the thread safely.

Throws:
IllegalStateException - thrown if the thread is not slated to be stopped
InterruptedException - propigated Thread.join() method exception.

stopInterruptJoin

public void stopInterruptJoin()
                       throws IllegalStateException,
                              InterruptedException
Joins with the thread, waiting for it to stop. If the thread hasn't stopped yet, it performs a hard stop, forcing the paint() method to quit.

This cleans up the thread safely.

Throws:
IllegalStateException - thrown if the thread is not slated to be stopped
InterruptedException - propigated Thread.join() method exception.

paintFinished

public void paintFinished(PaintFinishedEvent pfe)
                   throws InterruptedException
Processes a paint finished event. Need to do careful state checking here. We don't resume a paused thread, even though it would be the safe thing to do for dependent threads waiting on a queue or something. But this way, pausing the top level painter can pause the whole painting routine.

This interrupts the paint() method if it is painting, telling the painter to start over again.


killPainting

public void killPainting(KillPaintingEvent kpe)
This causes the current thread to stop as quickly as possible. It only returns after the thread has died, so all we need to do is generate a stopInterruptJoin() call, making sure to catch all exceptions that could be generated. Cleanup is automatically handled for us.

isInterrupted

protected void isInterrupted()
                      throws InterruptedException
Called inside a paint() method to check if an interrupt was generated. This should be the first method called in every paint method. If an interrupt was generated, then an InterruptedException is thrown, so no response checking is required in the method.
Throws:
InterruptedException - thrown if an interrupt had been generated.

canPause

private final void canPause()
                     throws IllegalStateException

canStop

private final void canStop()
                    throws IllegalStateException

isStopping

private final void isStopping()
                       throws IllegalStateException

fireMetricsWait

private final void fireMetricsWait(long duration)

fireMetricsPaint

private final void fireMetricsPaint(long duration)


Written under the LGPL