| /* |
| * Copyright (c) 1995, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.misc; |
| |
| /** |
| A Timer object is used by algorithms that require timed events. |
| For example, in an animation loop, a timer would help in |
| determining when to change frames. |
| |
| A timer has an interval which determines when it "ticks"; |
| that is, a timer delays for the specified interval and then |
| it calls the owner's tick() method. |
| |
| Here's an example of creating a timer with a 5 sec interval: |
| |
| <pre> |
| class Main implements Timeable { |
| public void tick(Timer timer) { |
| System.out.println("tick"); |
| } |
| public static void main(String args[]) { |
| (new Timer(this, 5000)).cont(); |
| } |
| } |
| </pre> |
| |
| A timer can be stopped, continued, or reset at any time. |
| A timer's state is not stopped while it's calling the |
| owner's tick() method. |
| |
| A timer can be regular or irregular. If in regular mode, |
| a timer ticks at the specified interval, regardless of |
| how long the owner's tick() method takes. While the timer |
| is running, no ticks are ever discarded. That means that if |
| the owner's tick() method takes longer than the interval, |
| the ticks that would have occurred are delivered immediately. |
| |
| In irregular mode, a timer starts delaying for exactly |
| the specified interval only after the tick() method returns. |
| |
| Synchronization issues: do not hold the timer's monitor |
| while calling any of the Timer operations below otherwise |
| the Timer class will deadlock. |
| |
| @author Patrick Chan |
| */ |
| |
| /* |
| Synchronization issues: there are two data structures that |
| require locking. A Timer object and the Timer queue |
| (described in the TimerThread class). To avoid deadlock, |
| the timer queue monitor is always acquired before the timer |
| object's monitor. However, the timer queue monitor is acquired |
| only if the timer operation will make use of the timer |
| queue, e.g. stop(). |
| |
| The class monitor on the class TimerThread severs as the monitor |
| to the timer queue. |
| |
| Possible feature: perhaps a timer should have an associated |
| thread priority. The thread that makes the callback temporarily |
| takes on that priority before calling the owner's tick() method. |
| */ |
| |
| public class Timer { |
| /** |
| * This is the owner of the timer. Its tick method is |
| * called when the timer ticks. |
| */ |
| public Timeable owner; |
| |
| /* |
| * This is the interval of time in ms. |
| */ |
| long interval; |
| |
| /* |
| * This variable is used for two different purposes. |
| * This is done in order to save space. |
| * If 'stopped' is true, this variable holds the time |
| * that the timer was stopped; otherwise, this variable |
| * is used by the TimerThread to determine when the timer |
| * should tick. |
| */ |
| long sleepUntil; |
| |
| /* |
| * This is the time remaining before the timer ticks. It |
| * is only valid if 'stopped' is true. If the timer is |
| * continued, the next tick will happen remaingTime |
| * milliseconds later. |
| */ |
| long remainingTime; |
| |
| /* |
| * True iff the timer is in regular mode. |
| */ |
| boolean regular; |
| |
| /* |
| * True iff the timer has been stopped. |
| */ |
| boolean stopped; |
| |
| /* ************************************************************** |
| * Timer queue-related variables |
| * ************************************************************** */ |
| |
| /* |
| * A link to another timer object. This is used while the |
| * timer object is enqueued in the timer queue. |
| */ |
| Timer next; |
| |
| /* ************************************************************** |
| * Timer methods |
| * ************************************************************** */ |
| |
| /* |
| * This variable holds a handle to the TimerThread class for |
| * the purpose of getting at the class monitor. The reason |
| * why Class.forName("TimerThread") is not used is because it |
| * doesn't appear to work when loaded via a net class loader. |
| */ |
| static TimerThread timerThread = null; |
| |
| /** |
| * Creates a timer object that is owned by 'owner' and |
| * with the interval 'interval' milliseconds. The new timer |
| * object is stopped and is regular. getRemainingTime() |
| * return 'interval' at this point. getStopTime() returns |
| * the time this object was created. |
| * @param owner owner of the timer object |
| * @param interval interval of the timer in milliseconds |
| */ |
| public Timer(Timeable owner, long interval) { |
| this.owner = owner; |
| this.interval = interval; |
| remainingTime = interval; |
| regular = true; |
| sleepUntil = System.currentTimeMillis(); |
| stopped = true; |
| synchronized (getClass()) { |
| if (timerThread == null) { |
| timerThread = new TimerThread(); |
| } |
| } |
| } |
| |
| /** |
| * Returns true if this timer is stopped. |
| */ |
| public synchronized boolean isStopped() { |
| return stopped; |
| } |
| |
| /** |
| * Stops the timer. The amount of time the timer has already |
| * delayed is saved so if the timer is continued, it will only |
| * delay for the amount of time remaining. |
| * Note that even after stopping a timer, one more tick may |
| * still occur. |
| * This method is MT-safe; i.e. it is synchronized but for |
| * implementation reasons, the synchronized modifier cannot |
| * be included in the method declaration. |
| */ |
| public void stop() { |
| long now = System.currentTimeMillis(); |
| |
| synchronized (timerThread) { |
| synchronized (this) { |
| if (!stopped) { |
| TimerThread.dequeue(this); |
| remainingTime = Math.max(0, sleepUntil - now); |
| sleepUntil = now; // stop time |
| stopped = true; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Continue the timer. The next tick will come at getRemainingTime() |
| * milliseconds later. If the timer is not stopped, this |
| * call will be a no-op. |
| * This method is MT-safe; i.e. it is synchronized but for |
| * implementation reasons, the synchronized modifier cannot |
| * be included in the method declaration. |
| */ |
| public void cont() { |
| synchronized (timerThread) { |
| synchronized (this) { |
| if (stopped) { |
| // The TimerTickThread avoids requeuing the |
| // timer only if the sleepUntil value has changed. |
| // The following guarantees that the sleepUntil |
| // value will be different; without this guarantee, |
| // it's theoretically possible for the timer to be |
| // inserted twice. |
| sleepUntil = Math.max(sleepUntil + 1, |
| System.currentTimeMillis() + remainingTime); |
| TimerThread.enqueue(this); |
| stopped = false; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Resets the timer's remaining time to the timer's interval. |
| * If the timer's running state is not altered. |
| */ |
| public void reset() { |
| synchronized (timerThread) { |
| synchronized (this) { |
| setRemainingTime(interval); |
| } |
| } |
| } |
| |
| /** |
| * Returns the time at which the timer was last stopped. The |
| * return value is valid only if the timer is stopped. |
| */ |
| public synchronized long getStopTime() { |
| return sleepUntil; |
| } |
| |
| /** |
| * Returns the timer's interval. |
| */ |
| public synchronized long getInterval() { |
| return interval; |
| } |
| |
| /** |
| * Changes the timer's interval. The new interval setting |
| * does not take effect until after the next tick. |
| * This method does not alter the remaining time or the |
| * running state of the timer. |
| * @param interval new interval of the timer in milliseconds |
| */ |
| public synchronized void setInterval(long interval) { |
| this.interval = interval; |
| } |
| |
| /** |
| * Returns the remaining time before the timer's next tick. |
| * The return value is valid only if timer is stopped. |
| */ |
| public synchronized long getRemainingTime() { |
| return remainingTime; |
| } |
| |
| /** |
| * Sets the remaining time before the timer's next tick. |
| * This method does not alter the timer's running state. |
| * This method is MT-safe; i.e. it is synchronized but for |
| * implementation reasons, the synchronized modifier cannot |
| * be included in the method declaration. |
| * @param time new remaining time in milliseconds. |
| */ |
| public void setRemainingTime(long time) { |
| synchronized (timerThread) { |
| synchronized (this) { |
| if (stopped) { |
| remainingTime = time; |
| } else { |
| stop(); |
| remainingTime = time; |
| cont(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * In regular mode, a timer ticks at the specified interval, |
| * regardless of how long the owner's tick() method takes. |
| * While the timer is running, no ticks are ever discarded. |
| * That means that if the owner's tick() method takes longer |
| * than the interval, the ticks that would have occurred are |
| * delivered immediately. |
| * |
| * In irregular mode, a timer starts delaying for exactly |
| * the specified interval only after the tick() method returns. |
| */ |
| public synchronized void setRegular(boolean regular) { |
| this.regular = regular; |
| } |
| |
| /* |
| * This method is used only for testing purposes. |
| */ |
| protected Thread getTimerThread() { |
| return TimerThread.timerThread; |
| } |
| } |
| |
| |
| /* |
| |
| This class implements the timer queue and is exclusively used by the |
| Timer class. There are only two methods exported to the Timer class - |
| enqueue, for inserting a timer into queue and dequeue, for removing |
| a timer from the queue. |
| |
| A timer in the timer queue is awaiting a tick. When a timer is to be |
| ticked, it is removed from the timer queue before the owner's tick() |
| method is called. |
| |
| A single timer thread manages the timer queue. This timer thread |
| looks at the head of the timer queue and delays until it's time for |
| the timer to tick. When the time comes, the timer thread creates a |
| callback thread to call the timer owner's tick() method. The timer |
| thread then processes the next timer in the queue. |
| |
| When a timer is inserted at the head of the queue, the timer thread is |
| notified. This causes the timer thread to prematurely wake up and |
| process the new head of the queue. |
| |
| */ |
| |
| class TimerThread extends Thread { |
| /* |
| * Set to true to get debugging output. |
| */ |
| public static boolean debug = false; |
| |
| /* |
| * This is a handle to the thread managing the thread queue. |
| */ |
| static TimerThread timerThread; |
| |
| /* |
| * This flag is set if the timer thread has been notified |
| * while it was in the timed wait. This flag allows the |
| * timer thread to tell whether or not the wait completed. |
| */ |
| static boolean notified = false; |
| |
| protected TimerThread() { |
| super("TimerThread"); |
| timerThread = this; |
| start(); |
| } |
| |
| public synchronized void run() { |
| while (true) { |
| long delay; |
| |
| while (timerQueue == null) { |
| try { |
| wait(); |
| } catch (InterruptedException ex) { |
| // Just drop through and check timerQueue. |
| } |
| } |
| notified = false; |
| delay = timerQueue.sleepUntil - System.currentTimeMillis(); |
| if (delay > 0) { |
| try { |
| wait(delay); |
| } catch (InterruptedException ex) { |
| // Just drop through. |
| } |
| } |
| // remove from timer queue. |
| if (!notified) { |
| Timer timer = timerQueue; |
| timerQueue = timerQueue.next; |
| TimerTickThread thr = TimerTickThread.call( |
| timer, timer.sleepUntil); |
| if (debug) { |
| long delta = (System.currentTimeMillis() - timer.sleepUntil); |
| System.out.println("tick(" + thr.getName() + "," |
| + timer.interval + ","+delta+ ")"); |
| if (delta > 250) { |
| System.out.println("*** BIG DELAY ***"); |
| } |
| } |
| } |
| } |
| } |
| |
| /* ******************************************************* |
| Timer Queue |
| ******************************************************* */ |
| |
| /* |
| * The timer queue is a queue of timers waiting to tick. |
| */ |
| static Timer timerQueue = null; |
| |
| /* |
| * Uses timer.sleepUntil to determine where in the queue |
| * to insert the timer object. |
| * A new ticker thread is created only if the timer |
| * is inserted at the beginning of the queue. |
| * The timer must not already be in the queue. |
| * Assumes the caller has the TimerThread monitor. |
| */ |
| static protected void enqueue(Timer timer) { |
| Timer prev = null; |
| Timer cur = timerQueue; |
| |
| if (cur == null || timer.sleepUntil <= cur.sleepUntil) { |
| // insert at front of queue |
| timer.next = timerQueue; |
| timerQueue = timer; |
| notified = true; |
| timerThread.notify(); |
| } else { |
| do { |
| prev = cur; |
| cur = cur.next; |
| } while (cur != null && timer.sleepUntil > cur.sleepUntil); |
| // insert or append to the timer queue |
| timer.next = cur; |
| prev.next = timer; |
| } |
| if (debug) { |
| long now = System.currentTimeMillis(); |
| |
| System.out.print(Thread.currentThread().getName() |
| + ": enqueue " + timer.interval + ": "); |
| cur = timerQueue; |
| while(cur != null) { |
| long delta = cur.sleepUntil - now; |
| System.out.print(cur.interval + "(" + delta + ") "); |
| cur = cur.next; |
| } |
| System.out.println(); |
| } |
| } |
| |
| /* |
| * If the timer is not in the queue, returns false; |
| * otherwise removes the timer from the timer queue and returns true. |
| * Assumes the caller has the TimerThread monitor. |
| */ |
| static protected boolean dequeue(Timer timer) { |
| Timer prev = null; |
| Timer cur = timerQueue; |
| |
| while (cur != null && cur != timer) { |
| prev = cur; |
| cur = cur.next; |
| } |
| if (cur == null) { |
| if (debug) { |
| System.out.println(Thread.currentThread().getName() |
| + ": dequeue " + timer.interval + ": no-op"); |
| } |
| return false; |
| } if (prev == null) { |
| timerQueue = timer.next; |
| notified = true; |
| timerThread.notify(); |
| } else { |
| prev.next = timer.next; |
| } |
| timer.next = null; |
| if (debug) { |
| long now = System.currentTimeMillis(); |
| |
| System.out.print(Thread.currentThread().getName() |
| + ": dequeue " + timer.interval + ": "); |
| cur = timerQueue; |
| while(cur != null) { |
| long delta = cur.sleepUntil - now; |
| System.out.print(cur.interval + "(" + delta + ") "); |
| cur = cur.next; |
| } |
| System.out.println(); |
| } |
| return true; |
| } |
| |
| /* |
| * Inserts the timer back into the queue. This method |
| * is used by a callback thread after it has called the |
| * timer owner's tick() method. This method recomputes |
| * the sleepUntil field. |
| * Assumes the caller has the TimerThread and Timer monitor. |
| */ |
| protected static void requeue(Timer timer) { |
| if (!timer.stopped) { |
| long now = System.currentTimeMillis(); |
| if (timer.regular) { |
| timer.sleepUntil += timer.interval; |
| } else { |
| timer.sleepUntil = now + timer.interval; |
| } |
| enqueue(timer); |
| } else if (debug) { |
| System.out.println(Thread.currentThread().getName() |
| + ": requeue " + timer.interval + ": no-op"); |
| } |
| } |
| } |
| |
| /* |
| |
| This class implements a simple thread whose only purpose is to call a |
| timer owner's tick() method. A small fixed-sized pool of threads is |
| maintained and is protected by the class monitor. If the pool is |
| exhausted, a new thread is temporarily created and destroyed when |
| done. |
| |
| A thread that's in the pool waits on it's own monitor. When the |
| thread is retrieved from the pool, the retriever notifies the thread's |
| monitor. |
| |
| */ |
| |
| class TimerTickThread extends Thread { |
| /* |
| * Maximum size of the thread pool. |
| */ |
| static final int MAX_POOL_SIZE = 3; |
| |
| /* |
| * Number of threads in the pool. |
| */ |
| static int curPoolSize = 0; |
| |
| /* |
| * The pool of timer threads. |
| */ |
| static TimerTickThread pool = null; |
| |
| /* |
| * Is used when linked into the thread pool. |
| */ |
| TimerTickThread next = null; |
| |
| /* |
| * This is the handle to the timer whose owner's |
| * tick() method will be called. |
| */ |
| Timer timer; |
| |
| /* |
| * The value of a timer's sleepUntil value is captured here. |
| * This is used to determine whether or not the timer should |
| * be reinserted into the queue. If the timer's sleepUntil |
| * value has changed, the timer is not reinserted. |
| */ |
| long lastSleepUntil; |
| |
| /* |
| * Creates a new callback thread to call the timer owner's |
| * tick() method. A thread is taken from the pool if one |
| * is available, otherwise, a new thread is created. |
| * The thread handle is returned. |
| */ |
| protected static synchronized TimerTickThread call( |
| Timer timer, long sleepUntil) { |
| TimerTickThread thread = pool; |
| |
| if (thread == null) { |
| // create one. |
| thread = new TimerTickThread(); |
| thread.timer = timer; |
| thread.lastSleepUntil = sleepUntil; |
| thread.start(); |
| } else { |
| pool = pool.next; |
| thread.timer = timer; |
| thread.lastSleepUntil = sleepUntil; |
| synchronized (thread) { |
| thread.notify(); |
| } |
| } |
| return thread; |
| } |
| |
| /* |
| * Returns false if the thread should simply exit; |
| * otherwise the thread is returned the pool, where |
| * it waits to be notified. (I did try to use the |
| * class monitor but the time between the notify |
| * and breaking out of the wait seemed to take |
| * significantly longer; need to look into this later.) |
| */ |
| private boolean returnToPool() { |
| synchronized (getClass()) { |
| if (curPoolSize >= MAX_POOL_SIZE) { |
| return false; |
| } |
| next = pool; |
| pool = this; |
| curPoolSize++; |
| timer = null; |
| } |
| while (timer == null) { |
| synchronized (this) { |
| try { |
| wait(); |
| } catch (InterruptedException ex) { |
| // Just drop through and retest timer. |
| } |
| } |
| } |
| synchronized (getClass()) { |
| curPoolSize--; |
| } |
| return true; |
| } |
| |
| public void run() { |
| do { |
| timer.owner.tick(timer); |
| synchronized (TimerThread.timerThread) { |
| synchronized (timer) { |
| if (lastSleepUntil == timer.sleepUntil) { |
| TimerThread.requeue(timer); |
| } |
| } |
| } |
| } while (returnToPool()); |
| } |
| } |