1. /*
  2. * @(#)TimerQueue.java 1.35 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.util.*;
  9. /**
  10. * Internal class to manage all Timers using one thread.
  11. * TimerQueue manages a queue of Timers. The Timers are chained
  12. * together in a linked list sorted by the order in which they will expire.
  13. *
  14. * @version 1.35 01/23/03
  15. * @author Dave Moore
  16. */
  17. class TimerQueue implements Runnable
  18. {
  19. private static final Object sharedInstanceKey =
  20. new StringBuffer("TimerQueue.sharedInstanceKey");
  21. private static final Object expiredTimersKey =
  22. new StringBuffer("TimerQueue.expiredTimersKey");
  23. Timer firstTimer;
  24. boolean running;
  25. /* Lock object used in place of class object for synchronization.
  26. * (4187686)
  27. */
  28. private static final Object classLock = new Object();
  29. /**
  30. * Constructor for TimerQueue.
  31. */
  32. public TimerQueue() {
  33. super();
  34. // Now start the TimerQueue thread.
  35. start();
  36. }
  37. public static TimerQueue sharedInstance() {
  38. synchronized (classLock) {
  39. TimerQueue sharedInst = (TimerQueue)
  40. SwingUtilities.appContextGet(
  41. sharedInstanceKey);
  42. if (sharedInst == null) {
  43. sharedInst = new TimerQueue();
  44. SwingUtilities.appContextPut(sharedInstanceKey, sharedInst);
  45. }
  46. return sharedInst;
  47. }
  48. }
  49. synchronized void start() {
  50. if (running) {
  51. throw new RuntimeException("Can't start a TimerQueue " +
  52. "that is already running");
  53. }
  54. else {
  55. SwingUtilities.doPrivileged(new Runnable() {
  56. public void run() {
  57. Thread timerThread = new Thread(TimerQueue.this,
  58. "TimerQueue");
  59. timerThread.setDaemon(true);
  60. timerThread.setPriority(Thread.NORM_PRIORITY);
  61. timerThread.start();
  62. }
  63. });
  64. running = true;
  65. }
  66. }
  67. synchronized void stop() {
  68. running = false;
  69. notify();
  70. }
  71. synchronized void addTimer(Timer timer, long expirationTime) {
  72. Timer previousTimer;
  73. Timer nextTimer;
  74. // If the Timer is already in the queue, then ignore the add.
  75. if (timer.running) {
  76. return;
  77. }
  78. previousTimer = null;
  79. nextTimer = firstTimer;
  80. // Insert the Timer into the linked list in the order they will
  81. // expire. If two timers expire at the same time, put the newer entry
  82. // later so they expire in the order they came in.
  83. while (nextTimer != null) {
  84. if (nextTimer.expirationTime > expirationTime) break;
  85. previousTimer = nextTimer;
  86. nextTimer = nextTimer.nextTimer;
  87. }
  88. if (previousTimer == null) {
  89. firstTimer = timer;
  90. }
  91. else {
  92. previousTimer.nextTimer = timer;
  93. }
  94. timer.expirationTime = expirationTime;
  95. timer.nextTimer = nextTimer;
  96. timer.running = true;
  97. notify();
  98. }
  99. synchronized void removeTimer(Timer timer) {
  100. Timer previousTimer;
  101. Timer nextTimer;
  102. boolean found;
  103. if (!timer.running) return;
  104. previousTimer = null;
  105. nextTimer = firstTimer;
  106. found = false;
  107. while (nextTimer != null) {
  108. if (nextTimer == timer) {
  109. found = true;
  110. break;
  111. }
  112. previousTimer = nextTimer;
  113. nextTimer = nextTimer.nextTimer;
  114. }
  115. if (!found) return;
  116. if (previousTimer == null) {
  117. firstTimer = timer.nextTimer;
  118. }
  119. else {
  120. previousTimer.nextTimer = timer.nextTimer;
  121. }
  122. timer.expirationTime = 0;
  123. timer.nextTimer = null;
  124. timer.running = false;
  125. }
  126. synchronized boolean containsTimer(Timer timer) {
  127. return timer.running;
  128. }
  129. /**
  130. * If there are a ton of timers, this method may never return. It loops
  131. * checking to see if the head of the Timer list has expired. If it has,
  132. * it posts the Timer and reschedules it if necessary.
  133. */
  134. synchronized long postExpiredTimers() {
  135. Timer timer;
  136. long currentTime;
  137. long timeToWait;
  138. // The timeToWait we return should never be negative and only be zero
  139. // when we have no Timers to wait for.
  140. do {
  141. timer = firstTimer;
  142. if (timer == null) return 0;
  143. currentTime = System.currentTimeMillis();
  144. timeToWait = timer.expirationTime - currentTime;
  145. if (timeToWait <= 0) {
  146. try {
  147. timer.post(); // have timer post an event
  148. }
  149. catch (SecurityException e) {
  150. }
  151. // Remove the timer from the queue
  152. removeTimer(timer);
  153. // This tries to keep the interval uniform at
  154. // the cost of drift.
  155. if (timer.isRepeats()) {
  156. addTimer(timer, currentTime + timer.getDelay());
  157. }
  158. // Allow other threads to call addTimer() and removeTimer()
  159. // even when we are posting Timers like mad. Since the wait()
  160. // releases the lock, be sure not to maintain any state
  161. // between iterations of the loop.
  162. try {
  163. wait(1);
  164. }
  165. catch (InterruptedException e) {
  166. }
  167. }
  168. } while (timeToWait <= 0);
  169. return timeToWait;
  170. }
  171. public synchronized void run() {
  172. long timeToWait;
  173. try {
  174. while (running) {
  175. timeToWait = postExpiredTimers();
  176. try {
  177. wait(timeToWait);
  178. }
  179. catch (InterruptedException e) {
  180. }
  181. }
  182. }
  183. catch (ThreadDeath td) {
  184. running = false;
  185. // Mark all the timers we contain as not being queued.
  186. Timer timer = firstTimer;
  187. while (timer != null) {
  188. timer.cancelEvent();
  189. timer = timer.nextTimer;
  190. }
  191. SystemEventQueueUtilities.restartTimerQueueThread();
  192. throw td;
  193. }
  194. }
  195. public synchronized String toString() {
  196. StringBuffer buf;
  197. Timer nextTimer;
  198. buf = new StringBuffer();
  199. buf.append("TimerQueue (");
  200. nextTimer = firstTimer;
  201. while (nextTimer != null) {
  202. buf.append(nextTimer.toString());
  203. nextTimer = nextTimer.nextTimer;
  204. if (nextTimer != null) buf.append(", ");
  205. }
  206. buf.append(")");
  207. return buf.toString();
  208. }
  209. }