383 lines · 13.5 KB · Java
RawDownload
/* * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt; import java.awt.AWTEvent;import java.util.Collections;import java.util.HashSet;import java.util.IdentityHashMap;import java.util.Map;import sun.util.logging.PlatformLogger; /** * This class is to let AWT shutdown automatically when a user is done * with AWT. It tracks AWT state using the following parameters: * <ul> * <li><code>peerMap</code> - the map between the existing peer objects *     and their associated targets * <li><code>toolkitThreadBusy</code> - whether the toolkit thread *     is waiting for a new native event to appear in its queue *     or is dispatching an event * <li><code>busyThreadSet</code> - a set of all the event dispatch *     threads that are busy at this moment, i.e. those that are not *     waiting for a new event to appear in their event queue. * </ul><p> * AWT is considered to be in ready-to-shutdown state when * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code> * is false and <code>busyThreadSet</code> is empty. * The internal AWTAutoShutdown logic secures that the single non-daemon * thread (<code>blockerThread</code>) is running when AWT is not in * ready-to-shutdown state. This blocker thread is to prevent AWT from * exiting since the toolkit thread is now daemon and all the event * dispatch threads are started only when needed. Once it is detected * that AWT is in ready-to-shutdown state this blocker thread waits * for a certain timeout and if AWT state doesn't change during timeout * this blocker thread terminates all the event dispatch threads and * exits. */public final class AWTAutoShutdown implements Runnable {     private static final AWTAutoShutdown theInstance = new AWTAutoShutdown();     /**     * This lock object is used to synchronize shutdown operations.     */    private final Object mainLock = new Object();     /**     * This lock object is to secure that when a new blocker thread is     * started it will be the first who acquire the main lock after     * the thread that created the new blocker released the main lock     * by calling lock.wait() to wait for the blocker to start.     */    private final Object activationLock = new Object();     /**     * This set keeps references to all the event dispatch threads that     * are busy at this moment, i.e. those that are not waiting for a     * new event to appear in their event queue.     * Access is synchronized on the main lock object.     */    private final HashSet busyThreadSet = new HashSet(7);     /**     * Indicates whether the toolkit thread is waiting for a new native     * event to appear or is dispatching an event.     */    private boolean toolkitThreadBusy = false;     /**     * This is a map between components and their peers.     * we should work with in under activationLock&mainLock lock.     */    private final Map peerMap = new IdentityHashMap();     /**     * References the alive non-daemon thread that is currently used     * for keeping AWT from exiting.     */    private Thread blockerThread = null;     /**     * We need this flag to secure that AWT state hasn't changed while     * we were waiting for the safety timeout to pass.     */    private boolean timeoutPassed = false;     /**     * Once we detect that AWT is ready to shutdown we wait for a certain     * timeout to pass before stopping event dispatch threads.     */    private static final int SAFETY_TIMEOUT = 1000;     /**     * Constructor method is intentionally made private to secure     * a single instance. Use getInstance() to reference it.     *     * @see     AWTAutoShutdown#getInstance     */    private AWTAutoShutdown() {}     /**     * Returns reference to a single AWTAutoShutdown instance.     */    public static AWTAutoShutdown getInstance() {        return theInstance;    }     /**     * Notify that the toolkit thread is not waiting for a native event     * to appear in its queue.     *     * @see     AWTAutoShutdown#notifyToolkitThreadFree     * @see     AWTAutoShutdown#setToolkitBusy     * @see     AWTAutoShutdown#isReadyToShutdown     */    public static void notifyToolkitThreadBusy() {        getInstance().setToolkitBusy(true);    }     /**     * Notify that the toolkit thread is waiting for a native event     * to appear in its queue.     *     * @see     AWTAutoShutdown#notifyToolkitThreadFree     * @see     AWTAutoShutdown#setToolkitBusy     * @see     AWTAutoShutdown#isReadyToShutdown     */    public static void notifyToolkitThreadFree() {        getInstance().setToolkitBusy(false);    }     /**     * Add a specified thread to the set of busy event dispatch threads.     * If this set already contains the specified thread or the thread is null,     * the call leaves this set unchanged and returns silently.     *     * @param thread thread to be added to this set, if not present.     * @see     AWTAutoShutdown#notifyThreadFree     * @see     AWTAutoShutdown#isReadyToShutdown     */    public void notifyThreadBusy(final Thread thread) {        if (thread == null) {            return;        }        synchronized (activationLock) {            synchronized (mainLock) {                if (blockerThread == null) {                    activateBlockerThread();                } else if (isReadyToShutdown()) {                    mainLock.notifyAll();                    timeoutPassed = false;                }                busyThreadSet.add(thread);            }        }    }     /**     * Remove a specified thread from the set of busy event dispatch threads.     * If this set doesn't contain the specified thread or the thread is null,     * the call leaves this set unchanged and returns silently.     *     * @param thread thread to be removed from this set, if present.     * @see     AWTAutoShutdown#notifyThreadBusy     * @see     AWTAutoShutdown#isReadyToShutdown     */    public void notifyThreadFree(final Thread thread) {        if (thread == null) {            return;        }        synchronized (activationLock) {            synchronized (mainLock) {                busyThreadSet.remove(thread);                if (isReadyToShutdown()) {                    mainLock.notifyAll();                    timeoutPassed = false;                }            }        }    }     /**     * Notify that the peermap has been updated, that means a new peer     * has been created or some existing peer has been disposed.     *     * @see     AWTAutoShutdown#isReadyToShutdown     */    void notifyPeerMapUpdated() {        synchronized (activationLock) {            synchronized (mainLock) {                if (!isReadyToShutdown() && blockerThread == null) {                    activateBlockerThread();                } else {                    mainLock.notifyAll();                    timeoutPassed = false;                }            }        }    }     /**     * Determine whether AWT is currently in ready-to-shutdown state.     * AWT is considered to be in ready-to-shutdown state if     * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>     * is false and <code>busyThreadSet</code> is empty.     *     * @return true if AWT is in ready-to-shutdown state.     */    private boolean isReadyToShutdown() {        return (!toolkitThreadBusy &&                 peerMap.isEmpty() &&                 busyThreadSet.isEmpty());    }     /**     * Notify about the toolkit thread state change.     *     * @param busy true if the toolkit thread state changes from idle     *             to busy.     * @see     AWTAutoShutdown#notifyToolkitThreadBusy     * @see     AWTAutoShutdown#notifyToolkitThreadFree     * @see     AWTAutoShutdown#isReadyToShutdown     */    private void setToolkitBusy(final boolean busy) {        if (busy != toolkitThreadBusy) {            synchronized (activationLock) {                synchronized (mainLock) {                    if (busy != toolkitThreadBusy) {                        if (busy) {                            if (blockerThread == null) {                                activateBlockerThread();                            } else if (isReadyToShutdown()) {                                mainLock.notifyAll();                                timeoutPassed = false;                            }                            toolkitThreadBusy = busy;                        } else {                            toolkitThreadBusy = busy;                            if (isReadyToShutdown()) {                                mainLock.notifyAll();                                timeoutPassed = false;                            }                        }                    }                }            }        }    }     /**     * Implementation of the Runnable interface.     * Incapsulates the blocker thread functionality.     *     * @see     AWTAutoShutdown#isReadyToShutdown     */    public void run() {        Thread currentThread = Thread.currentThread();        boolean interrupted = false;        synchronized (mainLock) {            try {                /* Notify that the thread is started. */                mainLock.notifyAll();                while (blockerThread == currentThread) {                    mainLock.wait();                    timeoutPassed = false;                    /*                     * This loop is introduced to handle the following case:                     * it is possible that while we are waiting for the                     * safety timeout to pass AWT state can change to                     * not-ready-to-shutdown and back to ready-to-shutdown.                     * In this case we have to wait once again.                     * NOTE: we shouldn't break into the outer loop                     * in this case, since we may never be notified                     * in an outer infinite wait at this point.                     */                    while (isReadyToShutdown()) {                        if (timeoutPassed) {                            timeoutPassed = false;                            blockerThread = null;                            break;                        }                        timeoutPassed = true;                        mainLock.wait(SAFETY_TIMEOUT);                    }                }            } catch (InterruptedException e) {                interrupted = true;            } finally {                if (blockerThread == currentThread) {                    blockerThread = null;                }            }        }        if (!interrupted) {            AppContext.stopEventDispatchThreads();        }    }     static AWTEvent getShutdownEvent() {        return new AWTEvent(getInstance(), 0) {};    }     /**     * Creates and starts a new blocker thread. Doesn't return until     * the new blocker thread starts.     */    private void activateBlockerThread() {        Thread thread = new Thread(this, "AWT-Shutdown");        thread.setDaemon(false);        blockerThread = thread;        thread.start();        try {            /* Wait for the blocker thread to start. */            mainLock.wait();        } catch (InterruptedException e) {            System.err.println("AWT blocker activation interrupted:");            e.printStackTrace();        }    }     final void registerPeer(final Object target, final Object peer) {        synchronized (activationLock) {            synchronized (mainLock) {                peerMap.put(target, peer);                notifyPeerMapUpdated();            }        }    }     final void unregisterPeer(final Object target, final Object peer) {        synchronized (activationLock) {            synchronized (mainLock) {                if (peerMap.get(target) == peer) {                    peerMap.remove(target);                    notifyPeerMapUpdated();                }            }        }    }     final Object getPeer(final Object target) {        synchronized (activationLock) {            synchronized (mainLock) {                return peerMap.get(target);            }        }    }     final void dumpPeers(final PlatformLogger aLog) {        synchronized (activationLock) {            synchronized (mainLock) {                aLog.fine("Mapped peers:");                for (Object key : peerMap.keySet()) {                    aLog.fine(key + "->" + peerMap.get(key));                }            }        }    } } // class AWTAutoShutdown