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
26package 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
88public 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
326This class implements the timer queue and is exclusively used by the
327Timer class. There are only two methods exported to the Timer class -
328enqueue, for inserting a timer into queue and dequeue, for removing
329a timer from the queue.
330
331A timer in the timer queue is awaiting a tick. When a timer is to be
332ticked, it is removed from the timer queue before the owner's tick()
333method is called.
334
335A single timer thread manages the timer queue. This timer thread
336looks at the head of the timer queue and delays until it's time for
337the timer to tick. When the time comes, the timer thread creates a
338callback thread to call the timer owner's tick() method. The timer
339thread then processes the next timer in the queue.
340
341When a timer is inserted at the head of the queue, the timer thread is
342notified. This causes the timer thread to prematurely wake up and
343process the new head of the queue.
344
345*/
346
347class 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
528This class implements a simple thread whose only purpose is to call a
529timer owner's tick() method. A small fixed-sized pool of threads is
530maintained and is protected by the class monitor. If the pool is
531exhausted, a new thread is temporarily created and destroyed when
532done.
533
534A thread that's in the pool waits on it's own monitor. When the
535thread is retrieved from the pool, the retriever notifies the thread's
536monitor.
537
538*/
539
540class 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}