001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt;
027:
028: import java.awt.AWTEvent;
029: import java.util.Collections;
030: import java.util.HashSet;
031: import java.util.IdentityHashMap;
032: import java.util.Map;
033: import java.util.logging.Logger;
034:
035: /**
036: * This class is to let AWT shutdown automatically when a user is done
037: * with AWT. It tracks AWT state using the following parameters:
038: * <ul>
039: * <li><code>peerMap</code> - the map between the existing peer objects
040: * and their associated targets
041: * <li><code>toolkitThreadBusy</code> - whether the toolkit thread
042: * is waiting for a new native event to appear in its queue
043: * or is dispatching an event
044: * <li><code>busyThreadSet</code> - a set of all the event dispatch
045: * threads that are busy at this moment, i.e. those that are not
046: * waiting for a new event to appear in their event queue.
047: * </ul><p>
048: * AWT is considered to be in ready-to-shutdown state when
049: * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>
050: * is false and <code>busyThreadSet</code> is empty.
051: * The internal AWTAutoShutdown logic secures that the single non-daemon
052: * thread (<code>blockerThread</code>) is running when AWT is not in
053: * ready-to-shutdown state. This blocker thread is to prevent AWT from
054: * exiting since the toolkit thread is now daemon and all the event
055: * dispatch threads are started only when needed. Once it is detected
056: * that AWT is in ready-to-shutdown state this blocker thread waits
057: * for a certain timeout and if AWT state doesn't change during timeout
058: * this blocker thread terminates all the event dispatch threads and
059: * exits.
060: */
061: public final class AWTAutoShutdown implements Runnable {
062:
063: private static final AWTAutoShutdown theInstance = new AWTAutoShutdown();
064:
065: /**
066: * This lock object is used to synchronize shutdown operations.
067: */
068: private final Object mainLock = new Object();
069:
070: /**
071: * This lock object is to secure that when a new blocker thread is
072: * started it will be the first who acquire the main lock after
073: * the thread that created the new blocker released the main lock
074: * by calling lock.wait() to wait for the blocker to start.
075: */
076: private final Object activationLock = new Object();
077:
078: /**
079: * This set keeps references to all the event dispatch threads that
080: * are busy at this moment, i.e. those that are not waiting for a
081: * new event to appear in their event queue.
082: * Access is synchronized on the main lock object.
083: */
084: private final HashSet busyThreadSet = new HashSet(7);
085:
086: /**
087: * Indicates whether the toolkit thread is waiting for a new native
088: * event to appear or is dispatching an event.
089: */
090: private boolean toolkitThreadBusy = false;
091:
092: /**
093: * This is a map between components and their peers.
094: * we should work with in under activationLock&mainLock lock.
095: */
096: private final Map peerMap = new IdentityHashMap();
097:
098: /**
099: * 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: /**
126: * Returns reference to a single AWTAutoShutdown instance.
127: */
128: public static AWTAutoShutdown getInstance() {
129: return theInstance;
130: }
131:
132: /**
133: * Notify that the toolkit thread is not waiting for a native event
134: * to appear in its queue.
135: *
136: * @see AWTAutoShutdown#notifyToolkitThreadFree
137: * @see AWTAutoShutdown#setToolkitBusy
138: * @see AWTAutoShutdown#isReadyToShutdown
139: */
140: public static void notifyToolkitThreadBusy() {
141: getInstance().setToolkitBusy(true);
142: }
143:
144: /**
145: * Notify that the toolkit thread is waiting for a native event
146: * to appear in its queue.
147: *
148: * @see AWTAutoShutdown#notifyToolkitThreadFree
149: * @see AWTAutoShutdown#setToolkitBusy
150: * @see AWTAutoShutdown#isReadyToShutdown
151: */
152: public static void notifyToolkitThreadFree() {
153: getInstance().setToolkitBusy(false);
154: }
155:
156: /**
157: * Add a specified thread to the set of busy event dispatch threads.
158: * If this set already contains the specified thread, the call leaves
159: * this set unchanged and returns silently.
160: *
161: * @param thread thread to be added to this set, if not present.
162: * @see AWTAutoShutdown#notifyThreadFree
163: * @see AWTAutoShutdown#isReadyToShutdown
164: */
165: public void notifyThreadBusy(final Thread thread) {
166: synchronized (activationLock) {
167: synchronized (mainLock) {
168: if (blockerThread == null) {
169: activateBlockerThread();
170: } else if (isReadyToShutdown()) {
171: mainLock.notifyAll();
172: timeoutPassed = false;
173: }
174: busyThreadSet.add(thread);
175: }
176: }
177: }
178:
179: /**
180: * Remove a specified thread from the set of busy event dispatch threads.
181: * If this set doesn't contain the specified thread, the call leaves
182: * this set unchanged and returns silently.
183: *
184: * @param thread thread to be removed from this set, if present.
185: * @see AWTAutoShutdown#notifyThreadBusy
186: * @see AWTAutoShutdown#isReadyToShutdown
187: */
188: public void notifyThreadFree(final Thread thread) {
189: synchronized (activationLock) {
190: synchronized (mainLock) {
191: busyThreadSet.remove(thread);
192: if (isReadyToShutdown()) {
193: mainLock.notifyAll();
194: timeoutPassed = false;
195: }
196: }
197: }
198: }
199:
200: /**
201: * Notify that the peermap has been updated, that means a new peer
202: * has been created or some existing peer has been disposed.
203: *
204: * @see AWTAutoShutdown#isReadyToShutdown
205: */
206: void notifyPeerMapUpdated() {
207: synchronized (activationLock) {
208: synchronized (mainLock) {
209: if (!isReadyToShutdown() && blockerThread == null) {
210: activateBlockerThread();
211: } else {
212: mainLock.notifyAll();
213: timeoutPassed = false;
214: }
215: }
216: }
217: }
218:
219: /**
220: * Determine whether AWT is currently in ready-to-shutdown state.
221: * AWT is considered to be in ready-to-shutdown state if
222: * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>
223: * is false and <code>busyThreadSet</code> is empty.
224: *
225: * @return true if AWT is in ready-to-shutdown state.
226: */
227: private boolean isReadyToShutdown() {
228: return (!toolkitThreadBusy && peerMap.isEmpty() && busyThreadSet
229: .isEmpty());
230: }
231:
232: /**
233: * Notify about the toolkit thread state change.
234: *
235: * @param busy true if the toolkit thread state changes from idle
236: * to busy.
237: * @see AWTAutoShutdown#notifyToolkitThreadBusy
238: * @see AWTAutoShutdown#notifyToolkitThreadFree
239: * @see AWTAutoShutdown#isReadyToShutdown
240: */
241: private void setToolkitBusy(final boolean busy) {
242: if (busy != toolkitThreadBusy) {
243: synchronized (activationLock) {
244: synchronized (mainLock) {
245: if (busy != toolkitThreadBusy) {
246: if (busy) {
247: if (blockerThread == null) {
248: activateBlockerThread();
249: } else if (isReadyToShutdown()) {
250: mainLock.notifyAll();
251: timeoutPassed = false;
252: }
253: toolkitThreadBusy = busy;
254: } else {
255: toolkitThreadBusy = busy;
256: if (isReadyToShutdown()) {
257: mainLock.notifyAll();
258: timeoutPassed = false;
259: }
260: }
261: }
262: }
263: }
264: }
265: }
266:
267: /**
268: * Implementation of the Runnable interface.
269: * Incapsulates the blocker thread functionality.
270: *
271: * @see AWTAutoShutdown#isReadyToShutdown
272: */
273: public void run() {
274: Thread currentThread = Thread.currentThread();
275: boolean interrupted = false;
276: synchronized (mainLock) {
277: try {
278: /* Notify that the thread is started. */
279: mainLock.notifyAll();
280: while (blockerThread == currentThread) {
281: mainLock.wait();
282: timeoutPassed = false;
283: /*
284: * This loop is introduced to handle the following case:
285: * it is possible that while we are waiting for the
286: * safety timeout to pass AWT state can change to
287: * not-ready-to-shutdown and back to ready-to-shutdown.
288: * In this case we have to wait once again.
289: * NOTE: we shouldn't break into the outer loop
290: * in this case, since we may never be notified
291: * in an outer infinite wait at this point.
292: */
293: while (isReadyToShutdown()) {
294: if (timeoutPassed) {
295: timeoutPassed = false;
296: blockerThread = null;
297: break;
298: }
299: timeoutPassed = true;
300: mainLock.wait(SAFETY_TIMEOUT);
301: }
302: }
303: } catch (InterruptedException e) {
304: interrupted = true;
305: } finally {
306: if (blockerThread == currentThread) {
307: blockerThread = null;
308: }
309: }
310: }
311: if (!interrupted) {
312: AppContext.stopEventDispatchThreads();
313: }
314: }
315:
316: static AWTEvent getShutdownEvent() {
317: return new AWTEvent(getInstance(), 0) {
318: };
319: }
320:
321: /**
322: * Creates and starts a new blocker thread. Doesn't return until
323: * the new blocker thread starts.
324: */
325: private void activateBlockerThread() {
326: Thread thread = new Thread(this , "AWT-Shutdown");
327: thread.setDaemon(false);
328: blockerThread = thread;
329: thread.start();
330: try {
331: /* Wait for the blocker thread to start. */
332: mainLock.wait();
333: } catch (InterruptedException e) {
334: System.err.println("AWT blocker activation interrupted:");
335: e.printStackTrace();
336: }
337: }
338:
339: final void registerPeer(final Object target, final Object peer) {
340: synchronized (activationLock) {
341: synchronized (mainLock) {
342: peerMap.put(target, peer);
343: notifyPeerMapUpdated();
344: }
345: }
346: }
347:
348: final void unregisterPeer(final Object target, final Object peer) {
349: synchronized (activationLock) {
350: synchronized (mainLock) {
351: if (peerMap.get(target) == peer) {
352: peerMap.remove(target);
353: notifyPeerMapUpdated();
354: }
355: }
356: }
357: }
358:
359: final Object getPeer(final Object target) {
360: synchronized (activationLock) {
361: synchronized (mainLock) {
362: return peerMap.get(target);
363: }
364: }
365: }
366:
367: final void dumpPeers(final Logger aLog) {
368: synchronized (activationLock) {
369: synchronized (mainLock) {
370: aLog.fine("Mapped peers:");
371: for (Object key : peerMap.keySet()) {
372: aLog.fine(key + "->" + peerMap.get(key));
373: }
374: }
375: }
376: }
377:
378: } // class AWTAutoShutdown
|