/* * 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
/*
* 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