1 /* 2 * Copyright (c) 2000, 2015, 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 package sun.awt; 27 28 import java.awt.AWTEvent; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import java.util.HashSet; 32 import java.util.IdentityHashMap; 33 import java.util.Map; 34 import java.util.Set; 35 36 import sun.awt.util.ThreadGroupUtils; 37 import sun.misc.ManagedLocalsThread; 38 import sun.util.logging.PlatformLogger; 39 40 /** 41 * This class is to let AWT shutdown automatically when a user is done 42 * with AWT. It tracks AWT state using the following parameters: 43 * <ul> 44 * <li><code>peerMap</code> - the map between the existing peer objects 45 * and their associated targets 46 * <li><code>toolkitThreadBusy</code> - whether the toolkit thread 47 * is waiting for a new native event to appear in its queue 48 * or is dispatching an event 49 * <li><code>busyThreadSet</code> - a set of all the event dispatch 50 * threads that are busy at this moment, i.e. those that are not 51 * waiting for a new event to appear in their event queue. 52 * </ul><p> 53 * AWT is considered to be in ready-to-shutdown state when 54 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code> 55 * is false and <code>busyThreadSet</code> is empty. 56 * The internal AWTAutoShutdown logic secures that the single non-daemon 57 * thread (<code>blockerThread</code>) is running when AWT is not in 58 * ready-to-shutdown state. This blocker thread is to prevent AWT from 59 * exiting since the toolkit thread is now daemon and all the event 60 * dispatch threads are started only when needed. Once it is detected 61 * that AWT is in ready-to-shutdown state this blocker thread waits 62 * for a certain timeout and if AWT state doesn't change during timeout 63 * this blocker thread terminates all the event dispatch threads and 64 * exits. 65 */ 66 public final class AWTAutoShutdown implements Runnable { 67 68 private static final AWTAutoShutdown theInstance = new AWTAutoShutdown(); 69 70 /** 71 * This lock object is used to synchronize shutdown operations. 72 */ 73 private final Object mainLock = new Object(); 74 75 /** 76 * This lock object is to secure that when a new blocker thread is 77 * started it will be the first who acquire the main lock after 78 * the thread that created the new blocker released the main lock 79 * by calling lock.wait() to wait for the blocker to start. 80 */ 81 private final Object activationLock = new Object(); 82 83 /** 84 * This set keeps references to all the event dispatch threads that 85 * are busy at this moment, i.e. those that are not waiting for a 86 * new event to appear in their event queue. 87 * Access is synchronized on the main lock object. 88 */ 89 private final Set<Thread> busyThreadSet = new HashSet<>(7); 90 91 /** 92 * Indicates whether the toolkit thread is waiting for a new native 93 * event to appear or is dispatching an event. 94 */ 95 private boolean toolkitThreadBusy = false; 96 97 /** 98 * This is a map between components and their peers. 99 * we should work with in under activationLock&mainLock lock. 100 */ 101 private final Map<Object, Object> peerMap = new IdentityHashMap<>(); 102 103 /** 104 * References the alive non-daemon thread that is currently used 105 * for keeping AWT from exiting. 106 */ 107 private Thread blockerThread = null; 108 109 /** 110 * We need this flag to secure that AWT state hasn't changed while 111 * we were waiting for the safety timeout to pass. 112 */ 113 private boolean timeoutPassed = false; 114 115 /** 116 * Once we detect that AWT is ready to shutdown we wait for a certain 117 * timeout to pass before stopping event dispatch threads. 118 */ 119 private static final int SAFETY_TIMEOUT = 1000; 120 121 /** 122 * Constructor method is intentionally made private to secure 123 * a single instance. Use getInstance() to reference it. 124 * 125 * @see AWTAutoShutdown#getInstance 126 */ 127 private AWTAutoShutdown() {} 128 129 /** 130 * Returns reference to a single AWTAutoShutdown instance. 131 */ 132 public static AWTAutoShutdown getInstance() { 133 return theInstance; 134 } 135 136 /** 137 * Notify that the toolkit thread is not waiting for a native event 138 * to appear in its queue. 139 * 140 * @see AWTAutoShutdown#notifyToolkitThreadFree 141 * @see AWTAutoShutdown#setToolkitBusy 142 * @see AWTAutoShutdown#isReadyToShutdown 143 */ 144 public static void notifyToolkitThreadBusy() { 145 getInstance().setToolkitBusy(true); 146 } 147 148 /** 149 * Notify that the toolkit thread is waiting for a native event 150 * to appear in its queue. 151 * 152 * @see AWTAutoShutdown#notifyToolkitThreadFree 153 * @see AWTAutoShutdown#setToolkitBusy 154 * @see AWTAutoShutdown#isReadyToShutdown 155 */ 156 public static void notifyToolkitThreadFree() { 157 getInstance().setToolkitBusy(false); 158 } 159 160 /** 161 * Add a specified thread to the set of busy event dispatch threads. 162 * If this set already contains the specified thread or the thread is null, 163 * the call leaves this set unchanged and returns silently. 164 * 165 * @param thread thread to be added to this set, if not present. 166 * @see AWTAutoShutdown#notifyThreadFree 167 * @see AWTAutoShutdown#isReadyToShutdown 168 */ 169 public void notifyThreadBusy(final Thread thread) { 170 if (thread == null) { 171 return; 172 } 173 synchronized (activationLock) { 174 synchronized (mainLock) { 175 if (blockerThread == null) { 176 activateBlockerThread(); 177 } else if (isReadyToShutdown()) { 178 mainLock.notifyAll(); 179 timeoutPassed = false; 180 } 181 busyThreadSet.add(thread); 182 } 183 } 184 } 185 186 /** 187 * Remove a specified thread from the set of busy event dispatch threads. 188 * If this set doesn't contain the specified thread or the thread is null, 189 * the call leaves this set unchanged and returns silently. 190 * 191 * @param thread thread to be removed from this set, if present. 192 * @see AWTAutoShutdown#notifyThreadBusy 193 * @see AWTAutoShutdown#isReadyToShutdown 194 */ 195 public void notifyThreadFree(final Thread thread) { 196 if (thread == null) { 197 return; 198 } 199 synchronized (activationLock) { 200 synchronized (mainLock) { 201 busyThreadSet.remove(thread); 202 if (isReadyToShutdown()) { 203 mainLock.notifyAll(); 204 timeoutPassed = false; 205 } 206 } 207 } 208 } 209 210 /** 211 * Notify that the peermap has been updated, that means a new peer 212 * has been created or some existing peer has been disposed. 213 * 214 * @see AWTAutoShutdown#isReadyToShutdown 215 */ 216 void notifyPeerMapUpdated() { 217 synchronized (activationLock) { 218 synchronized (mainLock) { 219 if (!isReadyToShutdown() && blockerThread == null) { 220 activateBlockerThread(); 221 } else { 222 mainLock.notifyAll(); 223 timeoutPassed = false; 224 } 225 } 226 } 227 } 228 229 /** 230 * Determine whether AWT is currently in ready-to-shutdown state. 231 * AWT is considered to be in ready-to-shutdown state if 232 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code> 233 * is false and <code>busyThreadSet</code> is empty. 234 * 235 * @return true if AWT is in ready-to-shutdown state. 236 */ 237 private boolean isReadyToShutdown() { 238 return (!toolkitThreadBusy && 239 peerMap.isEmpty() && 240 busyThreadSet.isEmpty()); 241 } 242 243 /** 244 * Notify about the toolkit thread state change. 245 * 246 * @param busy true if the toolkit thread state changes from idle 247 * to busy. 248 * @see AWTAutoShutdown#notifyToolkitThreadBusy 249 * @see AWTAutoShutdown#notifyToolkitThreadFree 250 * @see AWTAutoShutdown#isReadyToShutdown 251 */ 252 private void setToolkitBusy(final boolean busy) { 253 if (busy != toolkitThreadBusy) { 254 synchronized (activationLock) { 255 synchronized (mainLock) { 256 if (busy != toolkitThreadBusy) { 257 if (busy) { 258 if (blockerThread == null) { 259 activateBlockerThread(); 260 } else if (isReadyToShutdown()) { 261 mainLock.notifyAll(); 262 timeoutPassed = false; 263 } 264 toolkitThreadBusy = busy; 265 } else { 266 toolkitThreadBusy = busy; 267 if (isReadyToShutdown()) { 268 mainLock.notifyAll(); 269 timeoutPassed = false; 270 } 271 } 272 } 273 } 274 } 275 } 276 } 277 278 /** 279 * Implementation of the Runnable interface. 280 * Incapsulates the blocker thread functionality. 281 * 282 * @see AWTAutoShutdown#isReadyToShutdown 283 */ 284 public void run() { 285 Thread currentThread = Thread.currentThread(); 286 boolean interrupted = false; 287 synchronized (mainLock) { 288 try { 289 /* Notify that the thread is started. */ 290 mainLock.notifyAll(); 291 while (blockerThread == currentThread) { 292 mainLock.wait(); 293 timeoutPassed = false; 294 /* 295 * This loop is introduced to handle the following case: 296 * it is possible that while we are waiting for the 297 * safety timeout to pass AWT state can change to 298 * not-ready-to-shutdown and back to ready-to-shutdown. 299 * In this case we have to wait once again. 300 * NOTE: we shouldn't break into the outer loop 301 * in this case, since we may never be notified 302 * in an outer infinite wait at this point. 303 */ 304 while (isReadyToShutdown()) { 305 if (timeoutPassed) { 306 timeoutPassed = false; 307 blockerThread = null; 308 break; 309 } 310 timeoutPassed = true; 311 mainLock.wait(SAFETY_TIMEOUT); 312 } 313 } 314 } catch (InterruptedException e) { 315 interrupted = true; 316 } finally { 317 if (blockerThread == currentThread) { 318 blockerThread = null; 319 } 320 } 321 } 322 if (!interrupted) { 323 AppContext.stopEventDispatchThreads(); 324 } 325 } 326 327 @SuppressWarnings("serial") 328 static AWTEvent getShutdownEvent() { 329 return new AWTEvent(getInstance(), 0) { 330 }; 331 } 332 333 /** 334 * Creates and starts a new blocker thread. Doesn't return until 335 * the new blocker thread starts. 336 */ 337 private void activateBlockerThread() { 338 AccessController.doPrivileged((PrivilegedAction<Thread>) () -> { 339 String name = "AWT-Shutdown"; 340 Thread thread = new ManagedLocalsThread( 341 ThreadGroupUtils.getRootThreadGroup(), this, name); 342 thread.setContextClassLoader(null); 343 thread.setDaemon(false); 344 blockerThread = thread; 345 return thread; 346 }).start(); 347 try { 348 /* Wait for the blocker thread to start. */ 349 mainLock.wait(); 350 } catch (InterruptedException e) { 351 System.err.println("AWT blocker activation interrupted:"); 352 e.printStackTrace(); 353 } 354 } 355 356 void registerPeer(final Object target, final Object peer) { 357 synchronized (activationLock) { 358 synchronized (mainLock) { 359 peerMap.put(target, peer); 360 notifyPeerMapUpdated(); 361 } 362 } 363 } 364 365 void unregisterPeer(final Object target, final Object peer) { 366 synchronized (activationLock) { 367 synchronized (mainLock) { 368 if (peerMap.get(target) == peer) { 369 peerMap.remove(target); 370 notifyPeerMapUpdated(); 371 } 372 } 373 } 374 } 375 376 Object getPeer(final Object target) { 377 synchronized (activationLock) { 378 synchronized (mainLock) { 379 return peerMap.get(target); 380 } 381 } 382 } 383 384 void dumpPeers(final PlatformLogger aLog) { 385 if (aLog.isLoggable(PlatformLogger.Level.FINE)) { 386 synchronized (activationLock) { 387 synchronized (mainLock) { 388 aLog.fine("Mapped peers:"); 389 for (Object key : peerMap.keySet()) { 390 aLog.fine(key + "->" + peerMap.get(key)); 391 } 392 } 393 } 394 } 395 } 396 397 } // class AWTAutoShutdown --- EOF ---