Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/awt/AWTAutoShutdown.java
38827 views
/*1* Copyright (c) 2000, 2014, 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;2829import java.security.AccessController;30import java.security.PrivilegedAction;31import java.util.HashSet;32import java.util.IdentityHashMap;33import java.util.Map;34import java.util.Set;3536import sun.util.logging.PlatformLogger;37import sun.misc.ThreadGroupUtils;3839/**40* This class is to let AWT shutdown automatically when a user is done41* with AWT. It tracks AWT state using the following parameters:42* <ul>43* <li><code>peerMap</code> - the map between the existing peer objects44* and their associated targets45* <li><code>toolkitThreadBusy</code> - whether the toolkit thread46* is waiting for a new native event to appear in its queue47* or is dispatching an event48* <li><code>busyThreadSet</code> - a set of all the event dispatch49* threads that are busy at this moment, i.e. those that are not50* waiting for a new event to appear in their event queue.51* </ul><p>52* AWT is considered to be in ready-to-shutdown state when53* <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>54* is false and <code>busyThreadSet</code> is empty.55* The internal AWTAutoShutdown logic secures that the single non-daemon56* thread (<code>blockerThread</code>) is running when AWT is not in57* ready-to-shutdown state. This blocker thread is to prevent AWT from58* exiting since the toolkit thread is now daemon and all the event59* dispatch threads are started only when needed. Once it is detected60* that AWT is in ready-to-shutdown state this blocker thread waits61* for a certain timeout and if AWT state doesn't change during timeout62* this blocker thread terminates all the event dispatch threads and63* exits.64*/65public final class AWTAutoShutdown implements Runnable {6667private static final AWTAutoShutdown theInstance = new AWTAutoShutdown();6869/**70* This lock object is used to synchronize shutdown operations.71*/72private final Object mainLock = new Object();7374/**75* This lock object is to secure that when a new blocker thread is76* started it will be the first who acquire the main lock after77* the thread that created the new blocker released the main lock78* by calling lock.wait() to wait for the blocker to start.79*/80private final Object activationLock = new Object();8182/**83* This set keeps references to all the event dispatch threads that84* are busy at this moment, i.e. those that are not waiting for a85* new event to appear in their event queue.86* Access is synchronized on the main lock object.87*/88private final Set<Thread> busyThreadSet = new HashSet<>(7);8990/**91* Indicates whether the toolkit thread is waiting for a new native92* event to appear or is dispatching an event.93*/94private boolean toolkitThreadBusy = false;9596/**97* This is a map between components and their peers.98* we should work with in under activationLock&mainLock lock.99*/100private final Map<Object, Object> peerMap = new IdentityHashMap<>();101102/**103* References the alive non-daemon thread that is currently used104* for keeping AWT from exiting.105*/106private Thread blockerThread = null;107108/**109* We need this flag to secure that AWT state hasn't changed while110* we were waiting for the safety timeout to pass.111*/112private boolean timeoutPassed = false;113114/**115* Once we detect that AWT is ready to shutdown we wait for a certain116* timeout to pass before stopping event dispatch threads.117*/118private static final int SAFETY_TIMEOUT = 1000;119120/**121* Constructor method is intentionally made private to secure122* a single instance. Use getInstance() to reference it.123*124* @see AWTAutoShutdown#getInstance125*/126private AWTAutoShutdown() {}127128/**129* Returns reference to a single AWTAutoShutdown instance.130*/131public static AWTAutoShutdown getInstance() {132return theInstance;133}134135/**136* Notify that the toolkit thread is not waiting for a native event137* to appear in its queue.138*139* @see AWTAutoShutdown#notifyToolkitThreadFree140* @see AWTAutoShutdown#setToolkitBusy141* @see AWTAutoShutdown#isReadyToShutdown142*/143public static void notifyToolkitThreadBusy() {144getInstance().setToolkitBusy(true);145}146147/**148* Notify that the toolkit thread is waiting for a native event149* to appear in its queue.150*151* @see AWTAutoShutdown#notifyToolkitThreadFree152* @see AWTAutoShutdown#setToolkitBusy153* @see AWTAutoShutdown#isReadyToShutdown154*/155public static void notifyToolkitThreadFree() {156getInstance().setToolkitBusy(false);157}158159/**160* Add a specified thread to the set of busy event dispatch threads.161* If this set already contains the specified thread or the thread is null,162* the call leaves this set unchanged and returns silently.163*164* @param thread thread to be added to this set, if not present.165* @see AWTAutoShutdown#notifyThreadFree166* @see AWTAutoShutdown#isReadyToShutdown167*/168public void notifyThreadBusy(final Thread thread) {169if (thread == null) {170return;171}172synchronized (activationLock) {173synchronized (mainLock) {174if (blockerThread == null) {175activateBlockerThread();176} else if (isReadyToShutdown()) {177mainLock.notifyAll();178timeoutPassed = false;179}180busyThreadSet.add(thread);181}182}183}184185/**186* Remove a specified thread from the set of busy event dispatch threads.187* If this set doesn't contain the specified thread or the thread is null,188* the call leaves this set unchanged and returns silently.189*190* @param thread thread to be removed from this set, if present.191* @see AWTAutoShutdown#notifyThreadBusy192* @see AWTAutoShutdown#isReadyToShutdown193*/194public void notifyThreadFree(final Thread thread) {195if (thread == null) {196return;197}198synchronized (activationLock) {199synchronized (mainLock) {200busyThreadSet.remove(thread);201if (isReadyToShutdown()) {202mainLock.notifyAll();203timeoutPassed = false;204}205}206}207}208209/**210* Notify that the peermap has been updated, that means a new peer211* has been created or some existing peer has been disposed.212*213* @see AWTAutoShutdown#isReadyToShutdown214*/215void notifyPeerMapUpdated() {216synchronized (activationLock) {217synchronized (mainLock) {218if (!isReadyToShutdown() && blockerThread == null) {219AccessController.doPrivileged((PrivilegedAction<Void>) () -> {220activateBlockerThread();221return null;222});223} else {224mainLock.notifyAll();225timeoutPassed = false;226}227}228}229}230231/**232* Determine whether AWT is currently in ready-to-shutdown state.233* AWT is considered to be in ready-to-shutdown state if234* <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>235* is false and <code>busyThreadSet</code> is empty.236*237* @return true if AWT is in ready-to-shutdown state.238*/239private boolean isReadyToShutdown() {240return (!toolkitThreadBusy &&241peerMap.isEmpty() &&242busyThreadSet.isEmpty());243}244245/**246* Notify about the toolkit thread state change.247*248* @param busy true if the toolkit thread state changes from idle249* to busy.250* @see AWTAutoShutdown#notifyToolkitThreadBusy251* @see AWTAutoShutdown#notifyToolkitThreadFree252* @see AWTAutoShutdown#isReadyToShutdown253*/254private void setToolkitBusy(final boolean busy) {255if (busy != toolkitThreadBusy) {256synchronized (activationLock) {257synchronized (mainLock) {258if (busy != toolkitThreadBusy) {259if (busy) {260if (blockerThread == null) {261activateBlockerThread();262} else if (isReadyToShutdown()) {263mainLock.notifyAll();264timeoutPassed = false;265}266toolkitThreadBusy = busy;267} else {268toolkitThreadBusy = busy;269if (isReadyToShutdown()) {270mainLock.notifyAll();271timeoutPassed = false;272}273}274}275}276}277}278}279280/**281* Implementation of the Runnable interface.282* Incapsulates the blocker thread functionality.283*284* @see AWTAutoShutdown#isReadyToShutdown285*/286public void run() {287Thread currentThread = Thread.currentThread();288boolean interrupted = false;289synchronized (mainLock) {290try {291/* Notify that the thread is started. */292mainLock.notifyAll();293while (blockerThread == currentThread) {294mainLock.wait();295timeoutPassed = false;296/*297* This loop is introduced to handle the following case:298* it is possible that while we are waiting for the299* safety timeout to pass AWT state can change to300* not-ready-to-shutdown and back to ready-to-shutdown.301* In this case we have to wait once again.302* NOTE: we shouldn't break into the outer loop303* in this case, since we may never be notified304* in an outer infinite wait at this point.305*/306while (isReadyToShutdown()) {307if (timeoutPassed) {308timeoutPassed = false;309blockerThread = null;310break;311}312timeoutPassed = true;313mainLock.wait(SAFETY_TIMEOUT);314}315}316} catch (InterruptedException e) {317interrupted = true;318} finally {319if (blockerThread == currentThread) {320blockerThread = null;321}322}323}324if (!interrupted) {325AppContext.stopEventDispatchThreads();326}327}328329@SuppressWarnings("serial")330static AWTEvent getShutdownEvent() {331return new AWTEvent(getInstance(), 0) {332};333}334335/**336* Creates and starts a new blocker thread. Doesn't return until337* the new blocker thread starts.338*339* Must be called with {@link sun.security.util.SecurityConstants#MODIFY_THREADGROUP_PERMISSION}340*/341private void activateBlockerThread() {342Thread thread = new Thread(ThreadGroupUtils.getRootThreadGroup(), this, "AWT-Shutdown");343thread.setContextClassLoader(null);344thread.setDaemon(false);345blockerThread = thread;346thread.start();347try {348/* Wait for the blocker thread to start. */349mainLock.wait();350} catch (InterruptedException e) {351System.err.println("AWT blocker activation interrupted:");352e.printStackTrace();353}354}355356final void registerPeer(final Object target, final Object peer) {357synchronized (activationLock) {358synchronized (mainLock) {359peerMap.put(target, peer);360notifyPeerMapUpdated();361}362}363}364365final void unregisterPeer(final Object target, final Object peer) {366synchronized (activationLock) {367synchronized (mainLock) {368if (peerMap.get(target) == peer) {369peerMap.remove(target);370notifyPeerMapUpdated();371}372}373}374}375376final Object getPeer(final Object target) {377synchronized (activationLock) {378synchronized (mainLock) {379return peerMap.get(target);380}381}382}383384final void dumpPeers(final PlatformLogger aLog) {385if (aLog.isLoggable(PlatformLogger.Level.FINE)) {386synchronized (activationLock) {387synchronized (mainLock) {388aLog.fine("Mapped peers:");389for (Object key : peerMap.keySet()) {390aLog.fine(key + "->" + peerMap.get(key));391}392}393}394}395}396397} // class AWTAutoShutdown398399400