Path: blob/master/src/java.desktop/share/classes/sun/awt/AWTAutoShutdown.java
41152 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.awt.AWTEvent;28import java.security.AccessController;29import java.security.PrivilegedAction;30import java.util.HashSet;31import java.util.IdentityHashMap;32import java.util.Map;33import java.util.Set;3435import sun.awt.util.ThreadGroupUtils;36import sun.util.logging.PlatformLogger;3738/**39* This class is to let AWT shutdown automatically when a user is done40* with AWT. It tracks AWT state using the following parameters:41* <ul>42* <li>{@code peerMap} - the map between the existing peer objects43* and their associated targets44* <li>{@code toolkitThreadBusy} - whether the toolkit thread45* is waiting for a new native event to appear in its queue46* or is dispatching an event47* <li>{@code busyThreadSet} - a set of all the event dispatch48* threads that are busy at this moment, i.e. those that are not49* waiting for a new event to appear in their event queue.50* </ul><p>51* AWT is considered to be in ready-to-shutdown state when52* {@code peerMap} is empty and {@code toolkitThreadBusy}53* is false and {@code busyThreadSet} is empty.54* The internal AWTAutoShutdown logic secures that the single non-daemon55* thread ({@code blockerThread}) is running when AWT is not in56* ready-to-shutdown state. This blocker thread is to prevent AWT from57* exiting since the toolkit thread is now daemon and all the event58* dispatch threads are started only when needed. Once it is detected59* that AWT is in ready-to-shutdown state this blocker thread waits60* for a certain timeout and if AWT state doesn't change during timeout61* this blocker thread terminates all the event dispatch threads and62* exits.63*/64public final class AWTAutoShutdown implements Runnable {6566private static final AWTAutoShutdown theInstance = new AWTAutoShutdown();6768/**69* This lock object is used to synchronize shutdown operations.70*/71private final Object mainLock = new Object();7273/**74* This lock object is to secure that when a new blocker thread is75* started it will be the first who acquire the main lock after76* the thread that created the new blocker released the main lock77* by calling lock.wait() to wait for the blocker to start.78*/79private final Object activationLock = new Object();8081/**82* This set keeps references to all the event dispatch threads that83* are busy at this moment, i.e. those that are not waiting for a84* new event to appear in their event queue.85* Access is synchronized on the main lock object.86*/87private final Set<Thread> busyThreadSet = new HashSet<>(7);8889/**90* Indicates whether the toolkit thread is waiting for a new native91* event to appear or is dispatching an event.92*/93private boolean toolkitThreadBusy = false;9495/**96* This is a map between components and their peers.97* we should work with in under activationLock&mainLock lock.98*/99private final Map<Object, Object> peerMap = new IdentityHashMap<>();100101/**102* References the alive non-daemon thread that is currently used103* for keeping AWT from exiting.104*/105private Thread blockerThread = null;106107/**108* We need this flag to secure that AWT state hasn't changed while109* we were waiting for the safety timeout to pass.110*/111private boolean timeoutPassed = false;112113/**114* Once we detect that AWT is ready to shutdown we wait for a certain115* timeout to pass before stopping event dispatch threads.116*/117private static final int SAFETY_TIMEOUT = 1000;118119/**120* Constructor method is intentionally made private to secure121* a single instance. Use getInstance() to reference it.122*123* @see AWTAutoShutdown#getInstance124*/125private AWTAutoShutdown() {}126127/**128* Returns reference to a single AWTAutoShutdown instance.129*/130public static AWTAutoShutdown getInstance() {131return theInstance;132}133134/**135* Notify that the toolkit thread is not waiting for a native event136* to appear in its queue.137*138* @see AWTAutoShutdown#notifyToolkitThreadFree139* @see AWTAutoShutdown#setToolkitBusy140* @see AWTAutoShutdown#isReadyToShutdown141*/142public static void notifyToolkitThreadBusy() {143getInstance().setToolkitBusy(true);144}145146/**147* Notify that the toolkit thread is waiting for a native event148* to appear in its queue.149*150* @see AWTAutoShutdown#notifyToolkitThreadFree151* @see AWTAutoShutdown#setToolkitBusy152* @see AWTAutoShutdown#isReadyToShutdown153*/154public static void notifyToolkitThreadFree() {155getInstance().setToolkitBusy(false);156}157158/**159* Add a specified thread to the set of busy event dispatch threads.160* If this set already contains the specified thread or the thread is null,161* the call leaves this set unchanged and returns silently.162*163* @param thread thread to be added to this set, if not present.164* @see AWTAutoShutdown#notifyThreadFree165* @see AWTAutoShutdown#isReadyToShutdown166*/167public void notifyThreadBusy(final Thread thread) {168if (thread == null) {169return;170}171synchronized (activationLock) {172synchronized (mainLock) {173if (blockerThread == null) {174activateBlockerThread();175} else if (isReadyToShutdown()) {176mainLock.notifyAll();177timeoutPassed = false;178}179busyThreadSet.add(thread);180}181}182}183184/**185* Remove a specified thread from the set of busy event dispatch threads.186* If this set doesn't contain the specified thread or the thread is null,187* the call leaves this set unchanged and returns silently.188*189* @param thread thread to be removed from this set, if present.190* @see AWTAutoShutdown#notifyThreadBusy191* @see AWTAutoShutdown#isReadyToShutdown192*/193public void notifyThreadFree(final Thread thread) {194if (thread == null) {195return;196}197synchronized (activationLock) {198synchronized (mainLock) {199busyThreadSet.remove(thread);200if (isReadyToShutdown()) {201mainLock.notifyAll();202timeoutPassed = false;203}204}205}206}207208/**209* Notify that the peermap has been updated, that means a new peer210* has been created or some existing peer has been disposed.211*212* @see AWTAutoShutdown#isReadyToShutdown213*/214void notifyPeerMapUpdated() {215synchronized (activationLock) {216synchronized (mainLock) {217if (!isReadyToShutdown() && blockerThread == null) {218activateBlockerThread();219} else {220mainLock.notifyAll();221timeoutPassed = false;222}223}224}225}226227/**228* Determine whether AWT is currently in ready-to-shutdown state.229* AWT is considered to be in ready-to-shutdown state if230* {@code peerMap} is empty and {@code toolkitThreadBusy}231* is false and {@code busyThreadSet} is empty.232*233* @return true if AWT is in ready-to-shutdown state.234*/235private boolean isReadyToShutdown() {236return (!toolkitThreadBusy &&237peerMap.isEmpty() &&238busyThreadSet.isEmpty());239}240241/**242* Notify about the toolkit thread state change.243*244* @param busy true if the toolkit thread state changes from idle245* to busy.246* @see AWTAutoShutdown#notifyToolkitThreadBusy247* @see AWTAutoShutdown#notifyToolkitThreadFree248* @see AWTAutoShutdown#isReadyToShutdown249*/250private void setToolkitBusy(final boolean busy) {251if (busy != toolkitThreadBusy) {252synchronized (activationLock) {253synchronized (mainLock) {254if (busy != toolkitThreadBusy) {255if (busy) {256if (blockerThread == null) {257activateBlockerThread();258} else if (isReadyToShutdown()) {259mainLock.notifyAll();260timeoutPassed = false;261}262toolkitThreadBusy = busy;263} else {264toolkitThreadBusy = busy;265if (isReadyToShutdown()) {266mainLock.notifyAll();267timeoutPassed = false;268}269}270}271}272}273}274}275276/**277* Implementation of the Runnable interface.278* Incapsulates the blocker thread functionality.279*280* @see AWTAutoShutdown#isReadyToShutdown281*/282public void run() {283Thread currentThread = Thread.currentThread();284boolean interrupted = false;285synchronized (mainLock) {286try {287/* Notify that the thread is started. */288mainLock.notifyAll();289while (blockerThread == currentThread) {290mainLock.wait();291timeoutPassed = false;292/*293* This loop is introduced to handle the following case:294* it is possible that while we are waiting for the295* safety timeout to pass AWT state can change to296* not-ready-to-shutdown and back to ready-to-shutdown.297* In this case we have to wait once again.298* NOTE: we shouldn't break into the outer loop299* in this case, since we may never be notified300* in an outer infinite wait at this point.301*/302while (isReadyToShutdown()) {303if (timeoutPassed) {304timeoutPassed = false;305blockerThread = null;306break;307}308timeoutPassed = true;309mainLock.wait(SAFETY_TIMEOUT);310}311}312} catch (InterruptedException e) {313interrupted = true;314} finally {315if (blockerThread == currentThread) {316blockerThread = null;317}318}319}320if (!interrupted) {321AppContext.stopEventDispatchThreads();322}323}324325@SuppressWarnings("serial")326static AWTEvent getShutdownEvent() {327return new AWTEvent(getInstance(), 0) {328};329}330331/**332* Creates and starts a new blocker thread. Doesn't return until333* the new blocker thread starts.334*/335@SuppressWarnings("removal")336private void activateBlockerThread() {337AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {338String name = "AWT-Shutdown";339Thread thread = new Thread(340ThreadGroupUtils.getRootThreadGroup(), this, name, 0, false);341thread.setContextClassLoader(null);342thread.setDaemon(false);343blockerThread = thread;344return thread;345}).start();346try {347/* Wait for the blocker thread to start. */348mainLock.wait();349} catch (InterruptedException e) {350System.err.println("AWT blocker activation interrupted:");351e.printStackTrace();352}353}354355void registerPeer(final Object target, final Object peer) {356synchronized (activationLock) {357synchronized (mainLock) {358peerMap.put(target, peer);359notifyPeerMapUpdated();360}361}362}363364void unregisterPeer(final Object target, final Object peer) {365synchronized (activationLock) {366synchronized (mainLock) {367if (peerMap.get(target) == peer) {368peerMap.remove(target);369notifyPeerMapUpdated();370}371}372}373}374375Object getPeer(final Object target) {376synchronized (activationLock) {377synchronized (mainLock) {378return peerMap.get(target);379}380}381}382383void dumpPeers(final PlatformLogger aLog) {384if (aLog.isLoggable(PlatformLogger.Level.FINE)) {385synchronized (activationLock) {386synchronized (mainLock) {387aLog.fine("Mapped peers:");388for (Object key : peerMap.keySet()) {389aLog.fine(key + "->" + peerMap.get(key));390}391}392}393}394}395396} // class AWTAutoShutdown397398399