1 /* 2 * Copyright (c) 1997, 2014, 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.*; 33 import java.util.concurrent.*; 34 import java.util.concurrent.locks.*; 35 import java.util.concurrent.atomic.AtomicLong; 36 import java.util.stream.Collectors; 37 38 import sun.awt.AppContext; 39 40 41 42 /** 43 * Internal class to manage all Timers using one thread. 44 * TimerQueue manages a queue of Timers. The Timers are chained 45 * together in a linked list sorted by the order in which they will expire. 46 * 47 * @author Dave Moore 48 * @author Igor Kushnirskiy 49 */ 50 class TimerQueue implements Runnable 51 { 52 private static final Object sharedInstanceKey = 53 new StringBuffer("TimerQueue.sharedInstanceKey"); 54 private static final Object expiredTimersKey = 55 new StringBuffer("TimerQueue.expiredTimersKey"); 56 57 private final DelayQueue<DelayedTimer> queue; 58 private volatile boolean running; 59 private final Lock runningLock; 60 61 /* Lock object used in place of class object for synchronization. 62 * (4187686) 63 */ 64 private static final Object classLock = new Object(); 65 66 /** Base of nanosecond timings, to avoid wrapping */ 67 private static final long NANO_ORIGIN = System.nanoTime(); 68 69 /** 70 * Constructor for TimerQueue. 71 */ 72 public TimerQueue() { 73 super(); 74 queue = new DelayQueue<DelayedTimer>(); 75 // Now start the TimerQueue thread. 76 runningLock = new ReentrantLock(); 77 startIfNeeded(); 78 } 79 80 81 public static TimerQueue sharedInstance() { 82 synchronized (classLock) { 83 TimerQueue sharedInst = (TimerQueue) 84 SwingUtilities.appContextGet( 85 sharedInstanceKey); 86 if (sharedInst == null) { 87 sharedInst = new TimerQueue(); 88 SwingUtilities.appContextPut(sharedInstanceKey, sharedInst); 89 } 90 return sharedInst; 91 } 92 } 93 94 95 void startIfNeeded() { 96 if (! running) { 97 runningLock.lock(); 98 try { 99 final ThreadGroup threadGroup = 100 AppContext.getAppContext().getThreadGroup(); 101 java.security.AccessController.doPrivileged( 102 new java.security.PrivilegedAction<Object>() { 103 public Object run() { 104 Thread timerThread = new Thread(threadGroup, TimerQueue.this, 105 "TimerQueue"); 106 timerThread.setDaemon(true); 107 timerThread.setPriority(Thread.NORM_PRIORITY); 108 timerThread.start(); 109 return null; 110 } 111 }); 112 running = true; 113 } finally { 114 runningLock.unlock(); 115 } 116 } 117 } 118 119 void addTimer(Timer timer, long delayMillis) { 120 timer.getLock().lock(); 121 try { 122 // If the Timer is already in the queue, then ignore the add. 123 if (! containsTimer(timer)) { 124 addTimer(new DelayedTimer(timer, 125 TimeUnit.MILLISECONDS.toNanos(delayMillis) 126 + now())); 127 } 128 } finally { 129 timer.getLock().unlock(); 130 } 131 } 132 133 private void addTimer(DelayedTimer delayedTimer) { 134 assert delayedTimer != null && ! containsTimer(delayedTimer.getTimer()); 135 136 Timer timer = delayedTimer.getTimer(); 137 timer.getLock().lock(); 138 try { 139 timer.delayedTimer = delayedTimer; 140 queue.add(delayedTimer); 141 } finally { 142 timer.getLock().unlock(); 143 } 144 } 145 146 void removeTimer(Timer timer) { 147 timer.getLock().lock(); 148 try { 149 if (timer.delayedTimer != null) { 150 queue.remove(timer.delayedTimer); 151 timer.delayedTimer = null; 152 } 153 } finally { 154 timer.getLock().unlock(); 155 } 156 } 157 158 boolean containsTimer(Timer timer) { 159 timer.getLock().lock(); 160 try { 161 return timer.delayedTimer != null; 162 } finally { 163 timer.getLock().unlock(); 164 } 165 } 166 167 168 public void run() { 169 runningLock.lock(); 170 try { 171 while (running) { 172 try { 173 Timer timer = queue.take().getTimer(); 174 timer.getLock().lock(); 175 try { 176 DelayedTimer delayedTimer = timer.delayedTimer; 177 if (delayedTimer != null) { 178 /* 179 * Timer is not removed after we get it from 180 * the queue and before the lock on the timer is 181 * acquired 182 */ 183 timer.post(); // have timer post an event 184 timer.delayedTimer = null; 185 if (timer.isRepeats()) { 186 delayedTimer.setTime(now() 187 + TimeUnit.MILLISECONDS.toNanos( 188 timer.getDelay())); 189 addTimer(delayedTimer); 190 } 191 } 192 193 // Allow run other threads on systems without kernel threads 194 timer.getLock().newCondition().awaitNanos(1); 195 } catch (SecurityException ignore) { 196 } finally { 197 timer.getLock().unlock(); 198 } 199 } catch (InterruptedException ie) { 200 // Shouldn't ignore InterruptedExceptions here, so AppContext 201 // is disposed gracefully, see 6799345 for details 202 if (AppContext.getAppContext().isDisposed()) { 203 break; 204 } 205 } 206 } 207 } 208 catch (ThreadDeath td) { 209 // Mark all the timers we contain as not being queued. 210 for (DelayedTimer delayedTimer : queue) { 211 delayedTimer.getTimer().cancelEvent(); 212 } 213 throw td; 214 } finally { 215 running = false; 216 runningLock.unlock(); 217 } 218 } 219 220 221 public String toString() { 222 return queue.stream() 223 .map(delayedTimer -> delayedTimer.getTimer().toString()) 224 .collect(Collectors.joining(", ", "TimerQueue (", ")")); 225 } 226 227 /** 228 * Returns nanosecond time offset by origin 229 */ 230 private 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 }