package edu.umd.cs.piccolox.swt; import org.eclipse.swt.widgets.Display; /** * @author Lance Good */ public class SWTTimerQueue implements Runnable { static SWTTimerQueue instance; Display display = null; SWTTimer firstTimer; boolean running; /** * Constructor for TimerQueue. */ public SWTTimerQueue(Display display) { super(); this.display = display; // Now start the TimerQueue thread. start(); } public static SWTTimerQueue sharedInstance(Display display) { if (instance == null) { instance = new SWTTimerQueue(display); } return instance; } synchronized void start() { if (running) { throw new RuntimeException("Can't start a TimerQueue " + "that is already running"); } else { Display.getDefault().asyncExec(new Runnable() { public void run() { Thread timerThread = new Thread(SWTTimerQueue.this, "TimerQueue"); timerThread.setDaemon(true); timerThread.setPriority(Thread.NORM_PRIORITY); timerThread.start(); } }); running = true; } } synchronized void stop() { running = false; notify(); } synchronized void addTimer(SWTTimer timer, long expirationTime) { SWTTimer previousTimer; SWTTimer nextTimer; // If the Timer is already in the queue, then ignore the add. if (timer.running) { return; } previousTimer = null; nextTimer = firstTimer; // Insert the Timer into the linked list in the order they will // expire. If two timers expire at the same time, put the newer entry // later so they expire in the order they came in. while (nextTimer != null) { if (nextTimer.expirationTime > expirationTime) break; previousTimer = nextTimer; nextTimer = nextTimer.nextTimer; } if (previousTimer == null) { firstTimer = timer; } else { previousTimer.nextTimer = timer; } timer.expirationTime = expirationTime; timer.nextTimer = nextTimer; timer.running = true; notify(); } synchronized void removeTimer(SWTTimer timer) { SWTTimer previousTimer; SWTTimer nextTimer; boolean found; if (!timer.running) return; previousTimer = null; nextTimer = firstTimer; found = false; while (nextTimer != null) { if (nextTimer == timer) { found = true; break; } previousTimer = nextTimer; nextTimer = nextTimer.nextTimer; } if (!found) return; if (previousTimer == null) { firstTimer = timer.nextTimer; } else { previousTimer.nextTimer = timer.nextTimer; } timer.expirationTime = 0; timer.nextTimer = null; timer.running = false; } synchronized boolean containsTimer(SWTTimer timer) { 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. */ 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.expirationTime - currentTime; if (timeToWait <= 0) { try { timer.postOverride(); // have timer post an event } catch (SecurityException 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 (InterruptedException e) { } } } while (timeToWait <= 0); return timeToWait; } public synchronized void run() { long timeToWait; try { while (running) { timeToWait = postExpiredTimers(); try { wait(timeToWait); } catch (InterruptedException e) { } } } catch (ThreadDeath td) { running = false; // Mark all the timers we contain as not being queued. SWTTimer timer = firstTimer; while (timer != null) { timer.cancelEventOverride(); timer = timer.nextTimer; } display.asyncExec(new SWTTimerQueueRestart(display)); throw td; } } 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.nextTimer; 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 { boolean attemptedStart; Display display = null; public SWTTimerQueueRestart(Display display) { this.display = display; } public synchronized void run() { // Only try and restart the q once. if(!attemptedStart) { SWTTimerQueue q = SWTTimerQueue.sharedInstance(display); synchronized(q) { if(!q.running) q.start(); } attemptedStart = true; } } } }