1. /*
  2. * @(#)TimerQueue.java 1.28 01/11/29
  3. *
  4. * Copyright 2002 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.28 11/29/01
  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. Thread timerThread = new Thread(this, "TimerQueue");
  56. try {
  57. timerThread.setDaemon(true);
  58. }
  59. catch (SecurityException e) {
  60. }
  61. timerThread.start();
  62. running = true;
  63. }
  64. }
  65. synchronized void stop() {
  66. running = false;
  67. notify();
  68. }
  69. synchronized void addTimer(Timer timer, long expirationTime) {
  70. Timer previousTimer;
  71. Timer nextTimer;
  72. // If the Timer is already in the queue, then ignore the add.
  73. if (timer.running) {
  74. return;
  75. }
  76. previousTimer = null;
  77. nextTimer = firstTimer;
  78. // Insert the Timer into the linked list in the order they will
  79. // expire. If two timers expire at the same time, put the newer entry
  80. // later so they expire in the order they came in.
  81. while (nextTimer != null) {
  82. if (nextTimer.expirationTime > expirationTime) break;
  83. previousTimer = nextTimer;
  84. nextTimer = nextTimer.nextTimer;
  85. }
  86. if (previousTimer == null) {
  87. firstTimer = timer;
  88. }
  89. else {
  90. previousTimer.nextTimer = timer;
  91. }
  92. timer.expirationTime = expirationTime;
  93. timer.nextTimer = nextTimer;
  94. timer.running = true;
  95. notify();
  96. }
  97. synchronized void removeTimer(Timer timer) {
  98. Timer previousTimer;
  99. Timer nextTimer;
  100. boolean found;
  101. if (!timer.running) return;
  102. previousTimer = null;
  103. nextTimer = firstTimer;
  104. found = false;
  105. while (nextTimer != null) {
  106. if (nextTimer == timer) {
  107. found = true;
  108. break;
  109. }
  110. previousTimer = nextTimer;
  111. nextTimer = nextTimer.nextTimer;
  112. }
  113. if (!found) return;
  114. if (previousTimer == null) {
  115. firstTimer = timer.nextTimer;
  116. }
  117. else {
  118. previousTimer.nextTimer = timer.nextTimer;
  119. }
  120. timer.expirationTime = 0;
  121. timer.nextTimer = null;
  122. timer.running = false;
  123. }
  124. synchronized boolean containsTimer(Timer timer) {
  125. return timer.running;
  126. }
  127. /**
  128. * If there are a ton of timers, this method may never return. It loops
  129. * checking to see if the head of the Timer list has expired. If it has,
  130. * it posts the Timer and reschedules it if necessary.
  131. */
  132. synchronized long postExpiredTimers() {
  133. Timer timer;
  134. long currentTime;
  135. long timeToWait;
  136. // The timeToWait we return should never be negative and only be zero
  137. // when we have no Timers to wait for.
  138. do {
  139. timer = firstTimer;
  140. if (timer == null) return 0;
  141. currentTime = System.currentTimeMillis();
  142. timeToWait = timer.expirationTime - currentTime;
  143. if (timeToWait <= 0) {
  144. try {
  145. timer.post(); // have timer post an event
  146. }
  147. catch (SecurityException e) {
  148. }
  149. // Remove the timer from the queue
  150. removeTimer(timer);
  151. // This tries to keep the interval uniform at
  152. // the cost of drift.
  153. if (timer.isRepeats()) {
  154. addTimer(timer, currentTime + timer.getDelay());
  155. }
  156. }
  157. // Allow other threads to call addTimer() and removeTimer()
  158. // even when we are posting Timers like mad. Since the wait()
  159. // releases the lock, be sure not to maintain any state
  160. // between iterations of the loop.
  161. try {
  162. wait(1);
  163. }
  164. catch (InterruptedException e) {
  165. }
  166. } while (timeToWait <= 0);
  167. return timeToWait;
  168. }
  169. public synchronized void run() {
  170. long timeToWait;
  171. try {
  172. while (running) {
  173. timeToWait = postExpiredTimers();
  174. try {
  175. wait(timeToWait);
  176. }
  177. catch (InterruptedException e) {
  178. }
  179. }
  180. }
  181. catch (ThreadDeath td) {
  182. running = false;
  183. // Mark all the timers we contain as not being queued.
  184. Timer timer = firstTimer;
  185. while (timer != null) {
  186. timer.eventQueued = false;
  187. timer = timer.nextTimer;
  188. }
  189. SystemEventQueueUtilities.restartTimerQueueThread();
  190. throw td;
  191. }
  192. }
  193. public synchronized String toString() {
  194. StringBuffer buf;
  195. Timer nextTimer;
  196. buf = new StringBuffer();
  197. buf.append("TimerQueue (");
  198. nextTimer = firstTimer;
  199. while (nextTimer != null) {
  200. buf.append(nextTimer.toString());
  201. nextTimer = nextTimer.nextTimer;
  202. if (nextTimer != null) buf.append(", ");
  203. }
  204. buf.append(")");
  205. return buf.toString();
  206. }
  207. }