Timer.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. /* Timer.java -- Timer that runs TimerTasks at a later time.
  2. Copyright (C) 2000, 2001, 2005 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package java.util;
  32. /**
  33. * Timer that can run TimerTasks at a later time.
  34. * TimerTasks can be scheduled for one time execution at some time in the
  35. * future. They can be scheduled to be rescheduled at a time period after the
  36. * task was last executed. Or they can be scheduled to be executed repeatedly
  37. * at a fixed rate.
  38. * <p>
  39. * The normal scheduling will result in a more or less even delay in time
  40. * between successive executions, but the executions could drift in time if
  41. * the task (or other tasks) takes a long time to execute. Fixed delay
  42. * scheduling guarantees more or less that the task will be executed at a
  43. * specific time, but if there is ever a delay in execution then the period
  44. * between successive executions will be shorter. The first method of
  45. * repeated scheduling is preferred for repeated tasks in response to user
  46. * interaction, the second method of repeated scheduling is preferred for tasks
  47. * that act like alarms.
  48. * <p>
  49. * The Timer keeps a binary heap as a task priority queue which means that
  50. * scheduling and serving of a task in a queue of n tasks costs O(log n).
  51. *
  52. * @see TimerTask
  53. * @since 1.3
  54. * @author Mark Wielaard (mark@klomp.org)
  55. */
  56. public class Timer
  57. {
  58. /**
  59. * Priority Task Queue.
  60. * TimerTasks are kept in a binary heap.
  61. * The scheduler calls sleep() on the queue when it has nothing to do or
  62. * has to wait. A sleeping scheduler can be notified by calling interrupt()
  63. * which is automatically called by the enqueue(), cancel() and
  64. * timerFinalized() methods.
  65. */
  66. private static final class TaskQueue
  67. {
  68. /** Default size of this queue */
  69. private static final int DEFAULT_SIZE = 32;
  70. /** Whether to return null when there is nothing in the queue */
  71. private boolean nullOnEmpty;
  72. /**
  73. * The heap containing all the scheduled TimerTasks
  74. * sorted by the TimerTask.scheduled field.
  75. * Null when the stop() method has been called.
  76. */
  77. private TimerTask heap[];
  78. /**
  79. * The actual number of elements in the heap
  80. * Can be less then heap.length.
  81. * Note that heap[0] is used as a sentinel.
  82. */
  83. private int elements;
  84. /**
  85. * Creates a TaskQueue of default size without any elements in it.
  86. */
  87. public TaskQueue()
  88. {
  89. heap = new TimerTask[DEFAULT_SIZE];
  90. elements = 0;
  91. nullOnEmpty = false;
  92. }
  93. /**
  94. * Adds a TimerTask at the end of the heap.
  95. * Grows the heap if necessary by doubling the heap in size.
  96. */
  97. private void add(TimerTask task)
  98. {
  99. elements++;
  100. if (elements == heap.length)
  101. {
  102. TimerTask new_heap[] = new TimerTask[heap.length * 2];
  103. System.arraycopy(heap, 0, new_heap, 0, heap.length);
  104. heap = new_heap;
  105. }
  106. heap[elements] = task;
  107. }
  108. /**
  109. * Removes the last element from the heap.
  110. * Shrinks the heap in half if
  111. * elements+DEFAULT_SIZE/2 <= heap.length/4.
  112. */
  113. private void remove()
  114. {
  115. // clear the entry first
  116. heap[elements] = null;
  117. elements--;
  118. if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4))
  119. {
  120. TimerTask new_heap[] = new TimerTask[heap.length / 2];
  121. System.arraycopy(heap, 0, new_heap, 0, elements + 1);
  122. heap = new_heap;
  123. }
  124. }
  125. /**
  126. * Adds a task to the queue and puts it at the correct place
  127. * in the heap.
  128. */
  129. public synchronized void enqueue(TimerTask task)
  130. {
  131. // Check if it is legal to add another element
  132. if (heap == null)
  133. {
  134. throw new IllegalStateException
  135. ("cannot enqueue when stop() has been called on queue");
  136. }
  137. heap[0] = task; // sentinel
  138. add(task); // put the new task at the end
  139. // Now push the task up in the heap until it has reached its place
  140. int child = elements;
  141. int parent = child / 2;
  142. while (heap[parent].scheduled > task.scheduled)
  143. {
  144. heap[child] = heap[parent];
  145. child = parent;
  146. parent = child / 2;
  147. }
  148. // This is the correct place for the new task
  149. heap[child] = task;
  150. heap[0] = null; // clear sentinel
  151. // Maybe sched() is waiting for a new element
  152. this.notify();
  153. }
  154. /**
  155. * Returns the top element of the queue.
  156. * Can return null when no task is in the queue.
  157. */
  158. private TimerTask top()
  159. {
  160. if (elements == 0)
  161. {
  162. return null;
  163. }
  164. else
  165. {
  166. return heap[1];
  167. }
  168. }
  169. /**
  170. * Returns the top task in the Queue.
  171. * Removes the element from the heap and reorders the heap first.
  172. * Can return null when there is nothing in the queue.
  173. */
  174. public synchronized TimerTask serve()
  175. {
  176. // The task to return
  177. TimerTask task = null;
  178. while (task == null)
  179. {
  180. // Get the next task
  181. task = top();
  182. // return null when asked to stop
  183. // or if asked to return null when the queue is empty
  184. if ((heap == null) || (task == null && nullOnEmpty))
  185. {
  186. return null;
  187. }
  188. // Do we have a task?
  189. if (task != null)
  190. {
  191. // The time to wait until the task should be served
  192. long time = task.scheduled - System.currentTimeMillis();
  193. if (time > 0)
  194. {
  195. // This task should not yet be served
  196. // So wait until this task is ready
  197. // or something else happens to the queue
  198. task = null; // set to null to make sure we call top()
  199. try
  200. {
  201. this.wait(time);
  202. }
  203. catch (InterruptedException _)
  204. {
  205. }
  206. }
  207. }
  208. else
  209. {
  210. // wait until a task is added
  211. // or something else happens to the queue
  212. try
  213. {
  214. this.wait();
  215. }
  216. catch (InterruptedException _)
  217. {
  218. }
  219. }
  220. }
  221. // reconstruct the heap
  222. TimerTask lastTask = heap[elements];
  223. remove();
  224. // drop lastTask at the beginning and move it down the heap
  225. int parent = 1;
  226. int child = 2;
  227. heap[1] = lastTask;
  228. while (child <= elements)
  229. {
  230. if (child < elements)
  231. {
  232. if (heap[child].scheduled > heap[child + 1].scheduled)
  233. {
  234. child++;
  235. }
  236. }
  237. if (lastTask.scheduled <= heap[child].scheduled)
  238. break; // found the correct place (the parent) - done
  239. heap[parent] = heap[child];
  240. parent = child;
  241. child = parent * 2;
  242. }
  243. // this is the correct new place for the lastTask
  244. heap[parent] = lastTask;
  245. // return the task
  246. return task;
  247. }
  248. /**
  249. * When nullOnEmpty is true the serve() method will return null when
  250. * there are no tasks in the queue, otherwise it will wait until
  251. * a new element is added to the queue. It is used to indicate to
  252. * the scheduler that no new tasks will ever be added to the queue.
  253. */
  254. public synchronized void setNullOnEmpty(boolean nullOnEmpty)
  255. {
  256. this.nullOnEmpty = nullOnEmpty;
  257. this.notify();
  258. }
  259. /**
  260. * When this method is called the current and all future calls to
  261. * serve() will return null. It is used to indicate to the Scheduler
  262. * that it should stop executing since no more tasks will come.
  263. */
  264. public synchronized void stop()
  265. {
  266. this.heap = null;
  267. this.elements = 0;
  268. this.notify();
  269. }
  270. /**
  271. * Remove all canceled tasks from the queue.
  272. */
  273. public synchronized int purge()
  274. {
  275. int removed = 0;
  276. // Null out any elements that are canceled. Skip element 0 as
  277. // it is the sentinel.
  278. for (int i = elements; i > 0; --i)
  279. {
  280. if (heap[i].scheduled < 0)
  281. {
  282. ++removed;
  283. // Remove an element by pushing the appropriate child
  284. // into place, and then iterating to the bottom of the
  285. // tree.
  286. int index = i;
  287. while (heap[index] != null)
  288. {
  289. int child = 2 * index;
  290. if (child >= heap.length)
  291. {
  292. // Off end; we're done.
  293. heap[index] = null;
  294. break;
  295. }
  296. if (child + 1 >= heap.length || heap[child + 1] == null)
  297. {
  298. // Nothing -- we're done.
  299. }
  300. else if (heap[child] == null
  301. || (heap[child].scheduled
  302. > heap[child + 1].scheduled))
  303. ++child;
  304. heap[index] = heap[child];
  305. index = child;
  306. }
  307. }
  308. }
  309. // Make a new heap if we shrank enough.
  310. int newLen = heap.length;
  311. while (elements - removed + DEFAULT_SIZE / 2 <= newLen / 4)
  312. newLen /= 2;
  313. if (newLen != heap.length)
  314. {
  315. TimerTask[] newHeap = new TimerTask[newLen];
  316. System.arraycopy(heap, 0, newHeap, 0, elements + 1);
  317. heap = newHeap;
  318. }
  319. return removed;
  320. }
  321. } // TaskQueue
  322. /**
  323. * The scheduler that executes all the tasks on a particular TaskQueue,
  324. * reschedules any repeating tasks and that waits when no task has to be
  325. * executed immediately. Stops running when canceled or when the parent
  326. * Timer has been finalized and no more tasks have to be executed.
  327. */
  328. private static final class Scheduler implements Runnable
  329. {
  330. // The priority queue containing all the TimerTasks.
  331. private TaskQueue queue;
  332. /**
  333. * Creates a new Scheduler that will schedule the tasks on the
  334. * given TaskQueue.
  335. */
  336. public Scheduler(TaskQueue queue)
  337. {
  338. this.queue = queue;
  339. }
  340. public void run()
  341. {
  342. TimerTask task;
  343. while ((task = queue.serve()) != null)
  344. {
  345. // If this task has not been canceled
  346. if (task.scheduled >= 0)
  347. {
  348. // Mark execution time
  349. task.lastExecutionTime = task.scheduled;
  350. // Repeatable task?
  351. if (task.period < 0)
  352. {
  353. // Last time this task is executed
  354. task.scheduled = -1;
  355. }
  356. // Run the task
  357. try
  358. {
  359. task.run();
  360. }
  361. catch (ThreadDeath death)
  362. {
  363. // If an exception escapes, the Timer becomes invalid.
  364. queue.stop();
  365. throw death;
  366. }
  367. catch (Throwable t)
  368. {
  369. // If an exception escapes, the Timer becomes invalid.
  370. queue.stop();
  371. }
  372. }
  373. // Calculate next time and possibly re-enqueue.
  374. if (task.scheduled >= 0)
  375. {
  376. if (task.fixed)
  377. {
  378. task.scheduled += task.period;
  379. }
  380. else
  381. {
  382. task.scheduled = task.period + System.currentTimeMillis();
  383. }
  384. try
  385. {
  386. queue.enqueue(task);
  387. }
  388. catch (IllegalStateException ise)
  389. {
  390. // Ignore. Apparently the Timer queue has been stopped.
  391. }
  392. }
  393. }
  394. }
  395. } // Scheduler
  396. // Number of Timers created.
  397. // Used for creating nice Thread names.
  398. private static int nr;
  399. // The queue that all the tasks are put in.
  400. // Given to the scheduler
  401. private TaskQueue queue;
  402. // The Scheduler that does all the real work
  403. private Scheduler scheduler;
  404. // Used to run the scheduler.
  405. // Also used to checked if the Thread is still running by calling
  406. // thread.isAlive(). Sometimes a Thread is suddenly killed by the system
  407. // (if it belonged to an Applet).
  408. private Thread thread;
  409. // When cancelled we don't accept any more TimerTasks.
  410. private boolean canceled;
  411. /**
  412. * Creates a new Timer with a non daemon Thread as Scheduler, with normal
  413. * priority and a default name.
  414. */
  415. public Timer()
  416. {
  417. this(false);
  418. }
  419. /**
  420. * Creates a new Timer with a daemon Thread as scheduler if daemon is true,
  421. * with normal priority and a default name.
  422. */
  423. public Timer(boolean daemon)
  424. {
  425. this(daemon, Thread.NORM_PRIORITY);
  426. }
  427. /**
  428. * Create a new Timer whose Thread has the indicated name. It will have
  429. * normal priority and will not be a daemon thread.
  430. * @param name the name of the Thread
  431. * @since 1.5
  432. */
  433. public Timer(String name)
  434. {
  435. this(false, Thread.NORM_PRIORITY, name);
  436. }
  437. /**
  438. * Create a new Timer whose Thread has the indicated name. It will have
  439. * normal priority. The boolean argument controls whether or not it
  440. * will be a daemon thread.
  441. * @param name the name of the Thread
  442. * @param daemon true if the Thread should be a daemon thread
  443. * @since 1.5
  444. */
  445. public Timer(String name, boolean daemon)
  446. {
  447. this(daemon, Thread.NORM_PRIORITY, name);
  448. }
  449. /**
  450. * Creates a new Timer with a daemon Thread as scheduler if daemon is true,
  451. * with the priority given and a default name.
  452. */
  453. private Timer(boolean daemon, int priority)
  454. {
  455. this(daemon, priority, "Timer-" + (++nr));
  456. }
  457. /**
  458. * Creates a new Timer with a daemon Thread as scheduler if daemon is true,
  459. * with the priority and name given.E
  460. */
  461. private Timer(boolean daemon, int priority, String name)
  462. {
  463. canceled = false;
  464. queue = new TaskQueue();
  465. scheduler = new Scheduler(queue);
  466. thread = new Thread(scheduler, name);
  467. thread.setDaemon(daemon);
  468. thread.setPriority(priority);
  469. thread.start();
  470. }
  471. /**
  472. * Cancels the execution of the scheduler. If a task is executing it will
  473. * normally finish execution, but no other tasks will be executed and no
  474. * more tasks can be scheduled.
  475. */
  476. public void cancel()
  477. {
  478. canceled = true;
  479. queue.stop();
  480. }
  481. /**
  482. * Schedules the task at Time time, repeating every period
  483. * milliseconds if period is positive and at a fixed rate if fixed is true.
  484. *
  485. * @exception IllegalArgumentException if time is negative
  486. * @exception IllegalStateException if the task was already scheduled or
  487. * canceled or this Timer is canceled or the scheduler thread has died
  488. */
  489. private void schedule(TimerTask task, long time, long period, boolean fixed)
  490. {
  491. if (time < 0)
  492. throw new IllegalArgumentException("negative time");
  493. if (task.scheduled == 0 && task.lastExecutionTime == -1)
  494. {
  495. task.scheduled = time;
  496. task.period = period;
  497. task.fixed = fixed;
  498. }
  499. else
  500. {
  501. throw new IllegalStateException
  502. ("task was already scheduled or canceled");
  503. }
  504. if (!this.canceled && this.thread != null)
  505. {
  506. queue.enqueue(task);
  507. }
  508. else
  509. {
  510. throw new IllegalStateException
  511. ("timer was canceled or scheduler thread has died");
  512. }
  513. }
  514. private static void positiveDelay(long delay)
  515. {
  516. if (delay < 0)
  517. {
  518. throw new IllegalArgumentException("delay is negative");
  519. }
  520. }
  521. private static void positivePeriod(long period)
  522. {
  523. if (period < 0)
  524. {
  525. throw new IllegalArgumentException("period is negative");
  526. }
  527. }
  528. /**
  529. * Schedules the task at the specified data for one time execution.
  530. *
  531. * @exception IllegalArgumentException if date.getTime() is negative
  532. * @exception IllegalStateException if the task was already scheduled or
  533. * canceled or this Timer is canceled or the scheduler thread has died
  534. */
  535. public void schedule(TimerTask task, Date date)
  536. {
  537. long time = date.getTime();
  538. schedule(task, time, -1, false);
  539. }
  540. /**
  541. * Schedules the task at the specified date and reschedules the task every
  542. * period milliseconds after the last execution of the task finishes until
  543. * this timer or the task is canceled.
  544. *
  545. * @exception IllegalArgumentException if period or date.getTime() is
  546. * negative
  547. * @exception IllegalStateException if the task was already scheduled or
  548. * canceled or this Timer is canceled or the scheduler thread has died
  549. */
  550. public void schedule(TimerTask task, Date date, long period)
  551. {
  552. positivePeriod(period);
  553. long time = date.getTime();
  554. schedule(task, time, period, false);
  555. }
  556. /**
  557. * Schedules the task after the specified delay milliseconds for one time
  558. * execution.
  559. *
  560. * @exception IllegalArgumentException if delay or
  561. * System.currentTimeMillis + delay is negative
  562. * @exception IllegalStateException if the task was already scheduled or
  563. * canceled or this Timer is canceled or the scheduler thread has died
  564. */
  565. public void schedule(TimerTask task, long delay)
  566. {
  567. positiveDelay(delay);
  568. long time = System.currentTimeMillis() + delay;
  569. schedule(task, time, -1, false);
  570. }
  571. /**
  572. * Schedules the task after the delay milliseconds and reschedules the
  573. * task every period milliseconds after the last execution of the task
  574. * finishes until this timer or the task is canceled.
  575. *
  576. * @exception IllegalArgumentException if delay or period is negative
  577. * @exception IllegalStateException if the task was already scheduled or
  578. * canceled or this Timer is canceled or the scheduler thread has died
  579. */
  580. public void schedule(TimerTask task, long delay, long period)
  581. {
  582. positiveDelay(delay);
  583. positivePeriod(period);
  584. long time = System.currentTimeMillis() + delay;
  585. schedule(task, time, period, false);
  586. }
  587. /**
  588. * Schedules the task at the specified date and reschedules the task at a
  589. * fixed rate every period milliseconds until this timer or the task is
  590. * canceled.
  591. *
  592. * @exception IllegalArgumentException if period or date.getTime() is
  593. * negative
  594. * @exception IllegalStateException if the task was already scheduled or
  595. * canceled or this Timer is canceled or the scheduler thread has died
  596. */
  597. public void scheduleAtFixedRate(TimerTask task, Date date, long period)
  598. {
  599. positivePeriod(period);
  600. long time = date.getTime();
  601. schedule(task, time, period, true);
  602. }
  603. /**
  604. * Schedules the task after the delay milliseconds and reschedules the task
  605. * at a fixed rate every period milliseconds until this timer or the task
  606. * is canceled.
  607. *
  608. * @exception IllegalArgumentException if delay or
  609. * System.currentTimeMillis + delay is negative
  610. * @exception IllegalStateException if the task was already scheduled or
  611. * canceled or this Timer is canceled or the scheduler thread has died
  612. */
  613. public void scheduleAtFixedRate(TimerTask task, long delay, long period)
  614. {
  615. positiveDelay(delay);
  616. positivePeriod(period);
  617. long time = System.currentTimeMillis() + delay;
  618. schedule(task, time, period, true);
  619. }
  620. /**
  621. * Tells the scheduler that the Timer task died
  622. * so there will be no more new tasks scheduled.
  623. */
  624. protected void finalize() throws Throwable
  625. {
  626. queue.setNullOnEmpty(true);
  627. }
  628. /**
  629. * Removes all cancelled tasks from the queue.
  630. * @return the number of tasks removed
  631. * @since 1.5
  632. */
  633. public int purge()
  634. {
  635. return queue.purge();
  636. }
  637. }