History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/misc/Timer.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/misc/Timer.java+647−0
@@ -0,0 +1,647 @@
1+/*
2+ * Copyright (c) 1995, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.misc;
27+
28+/**
29+ A Timer object is used by algorithms that require timed events.
30+ For example, in an animation loop, a timer would help in
31+ determining when to change frames.
32+
33+ A timer has an interval which determines when it "ticks";
34+ that is, a timer delays for the specified interval and then
35+ it calls the owner's tick() method.
36+
37+ Here's an example of creating a timer with a 5 sec interval:
38+
39+ <pre>
40+ class Main implements Timeable {
41+ public void tick(Timer timer) {
42+ System.out.println("tick");
43+ }
44+ public static void main(String args[]) {
45+ (new Timer(this, 5000)).cont();
46+ }
47+ }
48+ </pre>
49+
50+ A timer can be stopped, continued, or reset at any time.
51+ A timer's state is not stopped while it's calling the
52+ owner's tick() method.
53+
54+ A timer can be regular or irregular. If in regular mode,
55+ a timer ticks at the specified interval, regardless of
56+ how long the owner's tick() method takes. While the timer
57+ is running, no ticks are ever discarded. That means that if
58+ the owner's tick() method takes longer than the interval,
59+ the ticks that would have occurred are delivered immediately.
60+
61+ In irregular mode, a timer starts delaying for exactly
62+ the specified interval only after the tick() method returns.
63+
64+ Synchronization issues: do not hold the timer's monitor
65+ while calling any of the Timer operations below otherwise
66+ the Timer class will deadlock.
67+
68+ @author Patrick Chan
69+*/
70+
71+/*
72+ Synchronization issues: there are two data structures that
73+ require locking. A Timer object and the Timer queue
74+ (described in the TimerThread class). To avoid deadlock,
75+ the timer queue monitor is always acquired before the timer
76+ object's monitor. However, the timer queue monitor is acquired
77+ only if the timer operation will make use of the timer
78+ queue, e.g. stop().
79+
80+ The class monitor on the class TimerThread severs as the monitor
81+ to the timer queue.
82+
83+ Possible feature: perhaps a timer should have an associated
84+ thread priority. The thread that makes the callback temporarily
85+ takes on that priority before calling the owner's tick() method.
86+*/
87+
88+public class Timer {
89+ /**
90+ * This is the owner of the timer. Its tick method is
91+ * called when the timer ticks.
92+ */
93+ public Timeable owner;
94+
95+ /*
96+ * This is the interval of time in ms.
97+ */
98+ long interval;
99+
100+ /*
101+ * This variable is used for two different purposes.
102+ * This is done in order to save space.
103+ * If 'stopped' is true, this variable holds the time
104+ * that the timer was stopped; otherwise, this variable
105+ * is used by the TimerThread to determine when the timer
106+ * should tick.
107+ */
108+ long sleepUntil;
109+
110+ /*
111+ * This is the time remaining before the timer ticks. It
112+ * is only valid if 'stopped' is true. If the timer is
113+ * continued, the next tick will happen remaingTime
114+ * milliseconds later.
115+ */
116+ long remainingTime;
117+
118+ /*
119+ * True iff the timer is in regular mode.
120+ */
121+ boolean regular;
122+
123+ /*
124+ * True iff the timer has been stopped.
125+ */
126+ boolean stopped;
127+
128+ /* **************************************************************
129+ * Timer queue-related variables
130+ * ************************************************************** */
131+
132+ /*
133+ * A link to another timer object. This is used while the
134+ * timer object is enqueued in the timer queue.
135+ */
136+ Timer next;
137+
138+ /* **************************************************************
139+ * Timer methods
140+ * ************************************************************** */
141+
142+ /*
143+ * This variable holds a handle to the TimerThread class for
144+ * the purpose of getting at the class monitor. The reason
145+ * why Class.forName("TimerThread") is not used is because it
146+ * doesn't appear to work when loaded via a net class loader.
147+ */
148+ static TimerThread timerThread = null;
149+
150+ /**
151+ * Creates a timer object that is owned by 'owner' and
152+ * with the interval 'interval' milliseconds. The new timer
153+ * object is stopped and is regular. getRemainingTime()
154+ * return 'interval' at this point. getStopTime() returns
155+ * the time this object was created.
156+ * @param owner owner of the timer object
157+ * @param interval interval of the timer in milliseconds
158+ */
159+ public Timer(Timeable owner, long interval) {
160+ this.owner = owner;
161+ this.interval = interval;
162+ remainingTime = interval;
163+ regular = true;
164+ sleepUntil = System.currentTimeMillis();
165+ stopped = true;
166+ synchronized (getClass()) {
167+ if (timerThread == null) {
168+ timerThread = new TimerThread();
169+ }
170+ }
171+ }
172+
173+ /**
174+ * Returns true if this timer is stopped.
175+ */
176+ public synchronized boolean isStopped() {
177+ return stopped;
178+ }
179+
180+ /**
181+ * Stops the timer. The amount of time the timer has already
182+ * delayed is saved so if the timer is continued, it will only
183+ * delay for the amount of time remaining.
184+ * Note that even after stopping a timer, one more tick may
185+ * still occur.
186+ * This method is MT-safe; i.e. it is synchronized but for
187+ * implementation reasons, the synchronized modifier cannot
188+ * be included in the method declaration.
189+ */
190+ public void stop() {
191+ long now = System.currentTimeMillis();
192+
193+ synchronized (timerThread) {
194+ synchronized (this) {
195+ if (!stopped) {
196+ TimerThread.dequeue(this);
197+ remainingTime = Math.max(0, sleepUntil - now);
198+ sleepUntil = now; // stop time
199+ stopped = true;
200+ }
201+ }
202+ }
203+ }
204+
205+ /**
206+ * Continue the timer. The next tick will come at getRemainingTime()
207+ * milliseconds later. If the timer is not stopped, this
208+ * call will be a no-op.
209+ * This method is MT-safe; i.e. it is synchronized but for
210+ * implementation reasons, the synchronized modifier cannot
211+ * be included in the method declaration.
212+ */
213+ public void cont() {
214+ synchronized (timerThread) {
215+ synchronized (this) {
216+ if (stopped) {
217+ // The TimerTickThread avoids requeuing the
218+ // timer only if the sleepUntil value has changed.
219+ // The following guarantees that the sleepUntil
220+ // value will be different; without this guarantee,
221+ // it's theoretically possible for the timer to be
222+ // inserted twice.
223+ sleepUntil = Math.max(sleepUntil + 1,
224+ System.currentTimeMillis() + remainingTime);
225+ TimerThread.enqueue(this);
226+ stopped = false;
227+ }
228+ }
229+ }
230+ }
231+
232+ /**
233+ * Resets the timer's remaining time to the timer's interval.
234+ * If the timer's running state is not altered.
235+ */
236+ public void reset() {
237+ synchronized (timerThread) {
238+ synchronized (this) {
239+ setRemainingTime(interval);
240+ }
241+ }
242+ }
243+
244+ /**
245+ * Returns the time at which the timer was last stopped. The
246+ * return value is valid only if the timer is stopped.
247+ */
248+ public synchronized long getStopTime() {
249+ return sleepUntil;
250+ }
251+
252+ /**
253+ * Returns the timer's interval.
254+ */
255+ public synchronized long getInterval() {
256+ return interval;
257+ }
258+
259+ /**
260+ * Changes the timer's interval. The new interval setting
261+ * does not take effect until after the next tick.
262+ * This method does not alter the remaining time or the
263+ * running state of the timer.
264+ * @param interval new interval of the timer in milliseconds
265+ */
266+ public synchronized void setInterval(long interval) {
267+ this.interval = interval;
268+ }
269+
270+ /**
271+ * Returns the remaining time before the timer's next tick.
272+ * The return value is valid only if timer is stopped.
273+ */
274+ public synchronized long getRemainingTime() {
275+ return remainingTime;
276+ }
277+
278+ /**
279+ * Sets the remaining time before the timer's next tick.
280+ * This method does not alter the timer's running state.
281+ * This method is MT-safe; i.e. it is synchronized but for
282+ * implementation reasons, the synchronized modifier cannot
283+ * be included in the method declaration.
284+ * @param time new remaining time in milliseconds.
285+ */
286+ public void setRemainingTime(long time) {
287+ synchronized (timerThread) {
288+ synchronized (this) {
289+ if (stopped) {
290+ remainingTime = time;
291+ } else {
292+ stop();
293+ remainingTime = time;
294+ cont();
295+ }
296+ }
297+ }
298+ }
299+
300+ /**
301+ * In regular mode, a timer ticks at the specified interval,
302+ * regardless of how long the owner's tick() method takes.
303+ * While the timer is running, no ticks are ever discarded.
304+ * That means that if the owner's tick() method takes longer
305+ * than the interval, the ticks that would have occurred are
306+ * delivered immediately.
307+ *
308+ * In irregular mode, a timer starts delaying for exactly
309+ * the specified interval only after the tick() method returns.
310+ */
311+ public synchronized void setRegular(boolean regular) {
312+ this.regular = regular;
313+ }
314+
315+ /*
316+ * This method is used only for testing purposes.
317+ */
318+ protected Thread getTimerThread() {
319+ return TimerThread.timerThread;
320+ }
321+}
322+
323+
324+/*
325+
326+This class implements the timer queue and is exclusively used by the
327+Timer class. There are only two methods exported to the Timer class -
328+enqueue, for inserting a timer into queue and dequeue, for removing
329+a timer from the queue.
330+
331+A timer in the timer queue is awaiting a tick. When a timer is to be
332+ticked, it is removed from the timer queue before the owner's tick()
333+method is called.
334+
335+A single timer thread manages the timer queue. This timer thread
336+looks at the head of the timer queue and delays until it's time for
337+the timer to tick. When the time comes, the timer thread creates a
338+callback thread to call the timer owner's tick() method. The timer
339+thread then processes the next timer in the queue.
340+
341+When a timer is inserted at the head of the queue, the timer thread is
342+notified. This causes the timer thread to prematurely wake up and
343+process the new head of the queue.
344+
345+*/
346+
347+class TimerThread extends Thread {
348+ /*
349+ * Set to true to get debugging output.
350+ */
351+ public static boolean debug = false;
352+
353+ /*
354+ * This is a handle to the thread managing the thread queue.
355+ */
356+ static TimerThread timerThread;
357+
358+ /*
359+ * This flag is set if the timer thread has been notified
360+ * while it was in the timed wait. This flag allows the
361+ * timer thread to tell whether or not the wait completed.
362+ */
363+ static boolean notified = false;
364+
365+ protected TimerThread() {
366+ super("TimerThread");
367+ timerThread = this;
368+ start();
369+ }
370+
371+ public synchronized void run() {
372+ while (true) {
373+ long delay;
374+
375+ while (timerQueue == null) {
376+ try {
377+ wait();
378+ } catch (InterruptedException ex) {
379+ // Just drop through and check timerQueue.
380+ }
381+ }
382+ notified = false;
383+ delay = timerQueue.sleepUntil - System.currentTimeMillis();
384+ if (delay > 0) {
385+ try {
386+ wait(delay);
387+ } catch (InterruptedException ex) {
388+ // Just drop through.
389+ }
390+ }
391+ // remove from timer queue.
392+ if (!notified) {
393+ Timer timer = timerQueue;
394+ timerQueue = timerQueue.next;
395+ TimerTickThread thr = TimerTickThread.call(
396+ timer, timer.sleepUntil);
397+ if (debug) {
398+ long delta = (System.currentTimeMillis() - timer.sleepUntil);
399+ System.out.println("tick(" + thr.getName() + ","
400+ + timer.interval + ","+delta+ ")");
401+ if (delta > 250) {
402+ System.out.println("*** BIG DELAY ***");
403+ }
404+ }
405+ }
406+ }
407+ }
408+
409+ /* *******************************************************
410+ Timer Queue
411+ ******************************************************* */
412+
413+ /*
414+ * The timer queue is a queue of timers waiting to tick.
415+ */
416+ static Timer timerQueue = null;
417+
418+ /*
419+ * Uses timer.sleepUntil to determine where in the queue
420+ * to insert the timer object.
421+ * A new ticker thread is created only if the timer
422+ * is inserted at the beginning of the queue.
423+ * The timer must not already be in the queue.
424+ * Assumes the caller has the TimerThread monitor.
425+ */
426+ static protected void enqueue(Timer timer) {
427+ Timer prev = null;
428+ Timer cur = timerQueue;
429+
430+ if (cur == null || timer.sleepUntil <= cur.sleepUntil) {
431+ // insert at front of queue
432+ timer.next = timerQueue;
433+ timerQueue = timer;
434+ notified = true;
435+ timerThread.notify();
436+ } else {
437+ do {
438+ prev = cur;
439+ cur = cur.next;
440+ } while (cur != null && timer.sleepUntil > cur.sleepUntil);
441+ // insert or append to the timer queue
442+ timer.next = cur;
443+ prev.next = timer;
444+ }
445+ if (debug) {
446+ long now = System.currentTimeMillis();
447+
448+ System.out.print(Thread.currentThread().getName()
449+ + ": enqueue " + timer.interval + ": ");
450+ cur = timerQueue;
451+ while(cur != null) {
452+ long delta = cur.sleepUntil - now;
453+ System.out.print(cur.interval + "(" + delta + ") ");
454+ cur = cur.next;
455+ }
456+ System.out.println();
457+ }
458+ }
459+
460+ /*
461+ * If the timer is not in the queue, returns false;
462+ * otherwise removes the timer from the timer queue and returns true.
463+ * Assumes the caller has the TimerThread monitor.
464+ */
465+ static protected boolean dequeue(Timer timer) {
466+ Timer prev = null;
467+ Timer cur = timerQueue;
468+
469+ while (cur != null && cur != timer) {
470+ prev = cur;
471+ cur = cur.next;
472+ }
473+ if (cur == null) {
474+ if (debug) {
475+ System.out.println(Thread.currentThread().getName()
476+ + ": dequeue " + timer.interval + ": no-op");
477+ }
478+ return false;
479+ } if (prev == null) {
480+ timerQueue = timer.next;
481+ notified = true;
482+ timerThread.notify();
483+ } else {
484+ prev.next = timer.next;
485+ }
486+ timer.next = null;
487+ if (debug) {
488+ long now = System.currentTimeMillis();
489+
490+ System.out.print(Thread.currentThread().getName()
491+ + ": dequeue " + timer.interval + ": ");
492+ cur = timerQueue;
493+ while(cur != null) {
494+ long delta = cur.sleepUntil - now;
495+ System.out.print(cur.interval + "(" + delta + ") ");
496+ cur = cur.next;
497+ }
498+ System.out.println();
499+ }
500+ return true;
501+ }
502+
503+ /*
504+ * Inserts the timer back into the queue. This method
505+ * is used by a callback thread after it has called the
506+ * timer owner's tick() method. This method recomputes
507+ * the sleepUntil field.
508+ * Assumes the caller has the TimerThread and Timer monitor.
509+ */
510+ protected static void requeue(Timer timer) {
511+ if (!timer.stopped) {
512+ long now = System.currentTimeMillis();
513+ if (timer.regular) {
514+ timer.sleepUntil += timer.interval;
515+ } else {
516+ timer.sleepUntil = now + timer.interval;
517+ }
518+ enqueue(timer);
519+ } else if (debug) {
520+ System.out.println(Thread.currentThread().getName()
521+ + ": requeue " + timer.interval + ": no-op");
522+ }
523+ }
524+}
525+
526+/*
527+
528+This class implements a simple thread whose only purpose is to call a
529+timer owner's tick() method. A small fixed-sized pool of threads is
530+maintained and is protected by the class monitor. If the pool is
531+exhausted, a new thread is temporarily created and destroyed when
532+done.
533+
534+A thread that's in the pool waits on it's own monitor. When the
535+thread is retrieved from the pool, the retriever notifies the thread's
536+monitor.
537+
538+*/
539+
540+class TimerTickThread extends Thread {
541+ /*
542+ * Maximum size of the thread pool.
543+ */
544+ static final int MAX_POOL_SIZE = 3;
545+
546+ /*
547+ * Number of threads in the pool.
548+ */
549+ static int curPoolSize = 0;
550+
551+ /*
552+ * The pool of timer threads.
553+ */
554+ static TimerTickThread pool = null;
555+
556+ /*
557+ * Is used when linked into the thread pool.
558+ */
559+ TimerTickThread next = null;
560+
561+ /*
562+ * This is the handle to the timer whose owner's
563+ * tick() method will be called.
564+ */
565+ Timer timer;
566+
567+ /*
568+ * The value of a timer's sleepUntil value is captured here.
569+ * This is used to determine whether or not the timer should
570+ * be reinserted into the queue. If the timer's sleepUntil
571+ * value has changed, the timer is not reinserted.
572+ */
573+ long lastSleepUntil;
574+
575+ /*
576+ * Creates a new callback thread to call the timer owner's
577+ * tick() method. A thread is taken from the pool if one
578+ * is available, otherwise, a new thread is created.
579+ * The thread handle is returned.
580+ */
581+ protected static synchronized TimerTickThread call(
582+ Timer timer, long sleepUntil) {
583+ TimerTickThread thread = pool;
584+
585+ if (thread == null) {
586+ // create one.
587+ thread = new TimerTickThread();
588+ thread.timer = timer;
589+ thread.lastSleepUntil = sleepUntil;
590+ thread.start();
591+ } else {
592+ pool = pool.next;
593+ thread.timer = timer;
594+ thread.lastSleepUntil = sleepUntil;
595+ synchronized (thread) {
596+ thread.notify();
597+ }
598+ }
599+ return thread;
600+ }
601+
602+ /*
603+ * Returns false if the thread should simply exit;
604+ * otherwise the thread is returned the pool, where
605+ * it waits to be notified. (I did try to use the
606+ * class monitor but the time between the notify
607+ * and breaking out of the wait seemed to take
608+ * significantly longer; need to look into this later.)
609+ */
610+ private boolean returnToPool() {
611+ synchronized (getClass()) {
612+ if (curPoolSize >= MAX_POOL_SIZE) {
613+ return false;
614+ }
615+ next = pool;
616+ pool = this;
617+ curPoolSize++;
618+ timer = null;
619+ }
620+ while (timer == null) {
621+ synchronized (this) {
622+ try {
623+ wait();
624+ } catch (InterruptedException ex) {
625+ // Just drop through and retest timer.
626+ }
627+ }
628+ }
629+ synchronized (getClass()) {
630+ curPoolSize--;
631+ }
632+ return true;
633+ }
634+
635+ public void run() {
636+ do {
637+ timer.owner.tick(timer);
638+ synchronized (TimerThread.timerThread) {
639+ synchronized (timer) {
640+ if (lastSleepUntil == timer.sleepUntil) {
641+ TimerThread.requeue(timer);
642+ }
643+ }
644+ }
645+ } while (returnToPool());
646+ }
647+}