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 } |
|