1 /* 2 * Copyright (c) 1997, 2009, 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 27 28 package javax.swing; 29 30 31 32 import java.util.concurrent.*; 33 import java.util.concurrent.locks.*; 34 import java.util.concurrent.atomic.AtomicLong; 35 import sun.awt.AppContext; 36 37 38 39 /** 40 * Internal class to manage all Timers using one thread. 41 * TimerQueue manages a queue of Timers. The Timers are chained 42 * together in a linked list sorted by the order in which they will expire. 43 * 44 * @author Dave Moore 45 * @author Igor Kushnirskiy 46 */ 47 class TimerQueue implements Runnable 48 { 49 private static final Object sharedInstanceKey = new Object(); // TimerQueue.sharedInstanceKey 50 51 private final DelayQueue<DelayedTimer> queue; 52 private volatile boolean running; 53 private final Lock runningLock; 54 55 /* Lock object used in place of class object for synchronization. 56 * (4187686) 57 */ 58 private static final Object classLock = new Object(); 59 60 /** Base of nanosecond timings, to avoid wrapping */ 61 private static final long NANO_ORIGIN = System.nanoTime(); 62 63 /** 64 * Constructor for TimerQueue. 65 */ 66 public TimerQueue() { 67 super(); 68 queue = new DelayQueue<DelayedTimer>(); 69 // Now start the TimerQueue thread. 70 runningLock = new ReentrantLock(); 71 startIfNeeded(); 72 } 73 74 75 public static TimerQueue sharedInstance() { 76 synchronized (classLock) { 77 TimerQueue sharedInst = (TimerQueue) 78 SwingUtilities.appContextGet( 79 sharedInstanceKey); 80 if (sharedInst == null) { 81 sharedInst = new TimerQueue(); 82 SwingUtilities.appContextPut(sharedInstanceKey, sharedInst); 83 } 84 return sharedInst; 85 } 86 } 87 88 89 void startIfNeeded() { 90 if (! running) { 91 runningLock.lock(); 92 try { 93 final ThreadGroup threadGroup = 94 AppContext.getAppContext().getThreadGroup(); 95 java.security.AccessController.doPrivileged( 96 new java.security.PrivilegedAction() { 97 public Object run() { 98 Thread timerThread = new Thread(threadGroup, TimerQueue.this, 99 "TimerQueue"); 100 timerThread.setDaemon(true); 101 timerThread.setPriority(Thread.NORM_PRIORITY); 102 timerThread.start(); 103 return null; 104 } 105 }); 106 running = true; 107 } finally { 108 runningLock.unlock(); 109 } 110 } 111 } 112 113 void addTimer(Timer timer, long delayMillis) { 114 timer.getLock().lock(); 115 try { 116 // If the Timer is already in the queue, then ignore the add. 117 if (! containsTimer(timer)) { 118 addTimer(new DelayedTimer(timer, 119 TimeUnit.MILLISECONDS.toNanos(delayMillis) 120 + now())); 121 } 122 } finally { 123 timer.getLock().unlock(); 124 } 125 } 126 127 private void addTimer(DelayedTimer delayedTimer) { 128 assert delayedTimer != null && ! containsTimer(delayedTimer.getTimer()); 129 130 Timer timer = delayedTimer.getTimer(); 131 timer.getLock().lock(); 132 try { 133 timer.delayedTimer = delayedTimer; 134 queue.add(delayedTimer); 135 } finally { 136 timer.getLock().unlock(); 137 } 138 } 139 140 void removeTimer(Timer timer) { 141 timer.getLock().lock(); 142 try { 143 if (timer.delayedTimer != null) { 144 queue.remove(timer.delayedTimer); 145 timer.delayedTimer = null; 146 } 147 } finally { 148 timer.getLock().unlock(); 149 } 150 } 151 152 boolean containsTimer(Timer timer) { 153 timer.getLock().lock(); 154 try { 155 return timer.delayedTimer != null; 156 } finally { 157 timer.getLock().unlock(); 158 } 159 } 160 161 162 public void run() { 163 runningLock.lock(); 164 try { 165 while (running) { 166 try { 167 Timer timer = queue.take().getTimer(); 168 timer.getLock().lock(); 169 try { 170 DelayedTimer delayedTimer = timer.delayedTimer; 171 if (delayedTimer != null) { 172 /* 173 * Timer is not removed after we get it from 174 * the queue and before the lock on the timer is 175 * acquired 176 */ 177 timer.post(); // have timer post an event 178 timer.delayedTimer = null; 179 if (timer.isRepeats()) { 180 delayedTimer.setTime(now() 181 + TimeUnit.MILLISECONDS.toNanos( 182 timer.getDelay())); 183 addTimer(delayedTimer); 184 } 185 } 186 } catch (SecurityException ignore) { 187 } finally { 188 timer.getLock().unlock(); 189 } 190 } catch (InterruptedException ie) { 191 // Shouldn't ignore InterruptedExceptions here, so AppContext 192 // is disposed gracefully, see 6799345 for details 193 if (AppContext.getAppContext().isDisposed()) { 194 break; 195 } 196 } 197 } 198 } 199 catch (ThreadDeath td) { 200 // Mark all the timers we contain as not being queued. 201 for (DelayedTimer delayedTimer : queue) { 202 delayedTimer.getTimer().cancelEvent(); 203 } 204 throw td; 205 } finally { 206 running = false; 207 runningLock.unlock(); 208 } 209 } 210 211 212 public String toString() { 213 StringBuilder buf = new StringBuilder(); 214 buf.append("TimerQueue ("); 215 boolean isFirst = true; 216 for (DelayedTimer delayedTimer : queue) { 217 if (! isFirst) { 218 buf.append(", "); 219 } 220 buf.append(delayedTimer.getTimer().toString()); 221 isFirst = false; 222 } 223 buf.append(")"); 224 return buf.toString(); 225 } 226 227 /** 228 * Returns nanosecond time offset by origin 229 */ 230 private final static long now() { 231 return System.nanoTime() - NANO_ORIGIN; 232 } 233 234 static class DelayedTimer implements Delayed { 235 // most of it copied from 236 // java.util.concurrent.ScheduledThreadPoolExecutor 237 238 /** 239 * Sequence number to break scheduling ties, and in turn to 240 * guarantee FIFO order among tied entries. 241 */ 242 private static final AtomicLong sequencer = new AtomicLong(0); 243 244 /** Sequence number to break ties FIFO */ 245 private final long sequenceNumber; 246 247 248 /** The time the task is enabled to execute in nanoTime units */ 249 private volatile long time; 250 251 private final Timer timer; 252 253 DelayedTimer(Timer timer, long nanos) { 254 this.timer = timer; 255 time = nanos; 256 sequenceNumber = sequencer.getAndIncrement(); 257 } 258 259 260 final public long getDelay(TimeUnit unit) { 261 return unit.convert(time - now(), TimeUnit.NANOSECONDS); 262 } 263 264 final void setTime(long nanos) { 265 time = nanos; 266 } 267 268 final Timer getTimer() { 269 return timer; 270 } 271 272 public int compareTo(Delayed other) { 273 if (other == this) { // compare zero ONLY if same object 274 return 0; 275 } 276 if (other instanceof DelayedTimer) { 277 DelayedTimer x = (DelayedTimer)other; 278 long diff = time - x.time; 279 if (diff < 0) { 280 return -1; 281 } else if (diff > 0) { 282 return 1; 283 } else if (sequenceNumber < x.sequenceNumber) { 284 return -1; 285 } else { 286 return 1; 287 } 288 } 289 long d = (getDelay(TimeUnit.NANOSECONDS) - 290 other.getDelay(TimeUnit.NANOSECONDS)); 291 return (d == 0) ? 0 : ((d < 0) ? -1 : 1); 292 } 293 } 294 }