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