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