org.piccolo2d.extras.swt.SWTTimerQueue Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2011, Piccolo2D project, http://piccolo2d.org
* Copyright (c) 1998-2008, University of Maryland
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
* contributors may be used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.piccolo2d.extras.swt;
import org.eclipse.swt.widgets.Display;
/**
* The SWTTimerQueue is a queue of timers. It has been implemented as a linked
* list of SWTTimer objects.
*
* @author Lance Good
*/
public class SWTTimerQueue implements Runnable {
private static SWTTimerQueue instance;
private final Display display;
private SWTTimer firstTimer;
private boolean running;
/**
* Creates a timer queue that will be attached the the provided display.
* It's Timers are expected to modify only this display, or none.
*
* @param display the display that will get updated by this queue's timers.
*/
public SWTTimerQueue(final Display display) {
this.display = display;
// Now start the TimerQueue thread.
start();
}
/**
* Returns the singleton instance of the SWTTimerQueue. Take note that even
* when called with different displays it will always return the same result
* as the first call.
*
* @param display display to associate with this Timer Queue's Activities
* @return singleton instance of SWTTimerQueue
*/
public static SWTTimerQueue sharedInstance(final Display display) {
if (instance == null) {
instance = new SWTTimerQueue(display);
}
return instance;
}
/**
* Starts the timer queue. If it is already running, a RuntimeException will
* be thrown.
*/
synchronized void start() {
if (running) {
throw new RuntimeException("Can't start a TimerQueue that is already running");
}
// Ensures that the Thread will be started from the display thread.
Display.getDefault().asyncExec(new Runnable() {
public void run() {
final Thread timerThread = new Thread(SWTTimerQueue.this, "TimerQueue");
timerThread.setDaemon(true);
timerThread.setPriority(Thread.NORM_PRIORITY);
timerThread.start();
}
});
running = true;
}
/**
* Stops the TimerQueue Thread.
*/
synchronized void stop() {
running = false;
notifyAll();
}
/**
* Adds the provided timer to the queue of scheduled timers.
*
* @param timer timer to add
* @param expirationTime time at which the timer is to be stopped and
* removed from the queue. Given in unix time.
*/
synchronized void addTimer(final SWTTimer timer, final long expirationTime) {
// If the Timer is already in the queue, then do nothing
if (!timer.isRunning()) {
insertTimer(timer, expirationTime);
timer.setExpirationTime(expirationTime);
timer.setRunning(true);
notifyAll();
}
}
/**
* Insert the Timer into the queue in the order they will expire. If
* multiple timers are set to expire at the same time, it will insert it
* after the last one; that way they expire in the order they came in.
*
* @param timer timer to insert into the queue
* @param expirationTime time in UNIX time at which the new timer should
* expire
*/
private void insertTimer(final SWTTimer timer, final long expirationTime) {
SWTTimer previousTimer = findLastTimerExpiringBefore(expirationTime);
if (previousTimer == null) {
firstTimer = timer;
}
else {
timer.setNextTimer(previousTimer.getNextTimer());
previousTimer.setNextTimer(timer);
}
}
/**
* Finds the last timer that will expire before or at the given expiration
* time. If there are multiple timers expiring at the same time, the last
* one in the queue will be returned.
*
* @param expirationTime expiration to compare against timers in the queue
* @return last timer that will expire before or at the given expiration
* time
*/
private SWTTimer findLastTimerExpiringBefore(final long expirationTime) {
SWTTimer previousTimer = null;
SWTTimer nextTimer = firstTimer;
while (nextTimer != null && nextTimer.getExpirationTime() > expirationTime) {
previousTimer = nextTimer;
nextTimer = nextTimer.getNextTimer();
}
return previousTimer;
}
/**
* Removes the provided timer from the Timer Queue. If it is not found, then
* nothing happens.
*
* @param timer timer to remove from the queue
*/
synchronized void removeTimer(final SWTTimer timer) {
if (!timer.isRunning()) {
return;
}
if (timer == firstTimer) {
firstTimer = timer.getNextTimer();
}
else {
SWTTimer previousTimer = findLastTimerBefore(timer);
if (previousTimer != null) {
previousTimer.setNextTimer(timer.getNextTimer());
}
}
timer.setExpirationTime(0);
timer.setNextTimer(null);
timer.setRunning(false);
}
/**
* Finds the timer that immediately precedes the provided timer in the
* queue.
*
* @param timer to search for
* @return timer immediately preceding found timer, or null if not found
*/
private SWTTimer findLastTimerBefore(final SWTTimer timer) {
SWTTimer previousTimer = null;
SWTTimer currentTimer = firstTimer;
while (currentTimer != null) {
if (currentTimer == timer) {
return previousTimer;
}
previousTimer = currentTimer;
currentTimer = currentTimer.getNextTimer();
}
return null;
}
/**
* Returns true if this timer queue contains the given timer.
*
* @param timer timer being checked
* @return true if timer is scheduled in this queue
*/
synchronized boolean containsTimer(final SWTTimer timer) {
// TODO: making this use isRunning without causing an infinite loop
return timer.running;
}
/**
* If there are a ton of timers, this method may never return. It loops
* checking to see if the head of the Timer list has expired. If it has, it
* posts the Timer and reschedules it if necessary.
*
* @return how long the app can take before it should invoke this method
* again.
*/
private synchronized long postExpiredTimers() {
SWTTimer timer;
long currentTime;
long timeToWait;
// The timeToWait we return should never be negative and only be zero
// when we have no Timers to wait for.
do {
timer = firstTimer;
if (timer == null) {
return 0;
}
currentTime = System.currentTimeMillis();
timeToWait = timer.getExpirationTime() - currentTime;
if (timeToWait <= 0) {
try {
timer.postOverride(); // have timer post an event
}
catch (final SecurityException e) {
throw new RuntimeException("Could not post event", e);
}
// Remove the timer from the queue
removeTimer(timer);
// This tries to keep the interval uniform at
// the cost of drift.
if (timer.isRepeats()) {
addTimer(timer, currentTime + timer.getDelay());
}
// Allow other threads to call addTimer() and removeTimer()
// even when we are posting Timers like mad. Since the wait()
// releases the lock, be sure not to maintain any state
// between iterations of the loop.
try {
wait(1);
}
catch (final InterruptedException e) {
// Nothing to do
}
}
} while (timeToWait <= 0);
return timeToWait;
}
/**
* Dispatches work to timers until the queue is told to stop running.
*/
public synchronized void run() {
long timeToWait;
try {
while (running) {
timeToWait = postExpiredTimers();
try {
wait(timeToWait);
}
catch (final InterruptedException e) {
// Nothing to do
}
}
}
catch (final ThreadDeath td) {
running = false;
// Mark all the timers we contain as not being queued.
SWTTimer timer = firstTimer;
while (timer != null) {
timer.cancelEventOverride();
timer = timer.getNextTimer();
}
display.asyncExec(new SWTTimerQueueRestart(display));
throw td;
}
}
/**
* Generates a string handy for debugging the contents of the timer queue.
*
* @return String representation of the queue for use in debugging
*/
public synchronized String toString() {
StringBuffer buf;
SWTTimer nextTimer;
buf = new StringBuffer();
buf.append("TimerQueue (");
nextTimer = firstTimer;
while (nextTimer != null) {
buf.append(nextTimer.toString());
nextTimer = nextTimer.getNextTimer();
if (nextTimer != null) {
buf.append(", ");
}
}
buf.append(")");
return buf.toString();
}
/**
* Runnable that will message the shared instance of the Timer Queue to
* restart.
*/
protected static class SWTTimerQueueRestart implements Runnable {
/** Tracks whether a restart has been attempted. */
private boolean attemptedStart;
private final Display display;
/**
* Constructs a QueueRestart Runnable that will message the Timer Queue
* to Restart.
*
* @param display display associated with the SWTTimerQueue
*/
public SWTTimerQueueRestart(final Display display) {
this.display = display;
}
/**
* Attempts to restart the queue associated with the display.
*/
public synchronized void run() {
if (attemptedStart) {
return;
}
final SWTTimerQueue q = SWTTimerQueue.sharedInstance(display);
synchronized (q) {
if (!q.running) {
q.start();
}
}
attemptedStart = true;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy