blob: 871da0ce3f52c6ec44d873c9df001d149a606fb6 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt;
27
28import java.awt.AWTEvent;
29import java.util.Collections;
30import java.util.HashSet;
31import java.util.IdentityHashMap;
32import java.util.Map;
33import java.util.logging.Logger;
34
35/**
36 * This class is to let AWT shutdown automatically when a user is done
37 * with AWT. It tracks AWT state using the following parameters:
38 * <ul>
39 * <li><code>peerMap</code> - the map between the existing peer objects
40 * and their associated targets
41 * <li><code>toolkitThreadBusy</code> - whether the toolkit thread
42 * is waiting for a new native event to appear in its queue
43 * or is dispatching an event
44 * <li><code>busyThreadSet</code> - a set of all the event dispatch
45 * threads that are busy at this moment, i.e. those that are not
46 * waiting for a new event to appear in their event queue.
47 * </ul><p>
48 * AWT is considered to be in ready-to-shutdown state when
49 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>
50 * is false and <code>busyThreadSet</code> is empty.
51 * The internal AWTAutoShutdown logic secures that the single non-daemon
52 * thread (<code>blockerThread</code>) is running when AWT is not in
53 * ready-to-shutdown state. This blocker thread is to prevent AWT from
54 * exiting since the toolkit thread is now daemon and all the event
55 * dispatch threads are started only when needed. Once it is detected
56 * that AWT is in ready-to-shutdown state this blocker thread waits
57 * for a certain timeout and if AWT state doesn't change during timeout
58 * this blocker thread terminates all the event dispatch threads and
59 * exits.
60 */
61public final class AWTAutoShutdown implements Runnable {
62
63 private static final AWTAutoShutdown theInstance = new AWTAutoShutdown();
64
65 /**
66 * This lock object is used to synchronize shutdown operations.
67 */
68 private final Object mainLock = new Object();
69
70 /**
71 * This lock object is to secure that when a new blocker thread is
72 * started it will be the first who acquire the main lock after
73 * the thread that created the new blocker released the main lock
74 * by calling lock.wait() to wait for the blocker to start.
75 */
76 private final Object activationLock = new Object();
77
78 /**
79 * This set keeps references to all the event dispatch threads that
80 * are busy at this moment, i.e. those that are not waiting for a
81 * new event to appear in their event queue.
82 * Access is synchronized on the main lock object.
83 */
84 private final HashSet busyThreadSet = new HashSet(7);
85
86 /**
87 * Indicates whether the toolkit thread is waiting for a new native
88 * event to appear or is dispatching an event.
89 */
90 private boolean toolkitThreadBusy = false;
91
92 /**
93 * This is a map between components and their peers.
94 * we should work with in under activationLock&mainLock lock.
95 */
96 private final Map peerMap = new IdentityHashMap();
97
98 /**
99 * 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 * Returns reference to a single AWTAutoShutdown instance.
126 */
127 public static AWTAutoShutdown getInstance() {
128 return theInstance;
129 }
130
131 /**
132 * Notify that the toolkit thread is not waiting for a native event
133 * to appear in its queue.
134 *
135 * @see AWTAutoShutdown#notifyToolkitThreadFree
136 * @see AWTAutoShutdown#setToolkitBusy
137 * @see AWTAutoShutdown#isReadyToShutdown
138 */
139 public static void notifyToolkitThreadBusy() {
140 getInstance().setToolkitBusy(true);
141 }
142
143 /**
144 * Notify that the toolkit thread is waiting for a native event
145 * to appear in its queue.
146 *
147 * @see AWTAutoShutdown#notifyToolkitThreadFree
148 * @see AWTAutoShutdown#setToolkitBusy
149 * @see AWTAutoShutdown#isReadyToShutdown
150 */
151 public static void notifyToolkitThreadFree() {
152 getInstance().setToolkitBusy(false);
153 }
154
155 /**
156 * Add a specified thread to the set of busy event dispatch threads.
157 * If this set already contains the specified thread, the call leaves
158 * this set unchanged and returns silently.
159 *
160 * @param thread thread to be added to this set, if not present.
161 * @see AWTAutoShutdown#notifyThreadFree
162 * @see AWTAutoShutdown#isReadyToShutdown
163 */
164 public void notifyThreadBusy(final Thread thread) {
165 synchronized (activationLock) {
166 synchronized (mainLock) {
167 if (blockerThread == null) {
168 activateBlockerThread();
169 } else if (isReadyToShutdown()) {
170 mainLock.notifyAll();
171 timeoutPassed = false;
172 }
173 busyThreadSet.add(thread);
174 }
175 }
176 }
177
178 /**
179 * Remove a specified thread from the set of busy event dispatch threads.
180 * If this set doesn't contain the specified thread, the call leaves
181 * this set unchanged and returns silently.
182 *
183 * @param thread thread to be removed from this set, if present.
184 * @see AWTAutoShutdown#notifyThreadBusy
185 * @see AWTAutoShutdown#isReadyToShutdown
186 */
187 public void notifyThreadFree(final Thread thread) {
188 synchronized (activationLock) {
189 synchronized (mainLock) {
190 busyThreadSet.remove(thread);
191 if (isReadyToShutdown()) {
192 mainLock.notifyAll();
193 timeoutPassed = false;
194 }
195 }
196 }
197 }
198
199 /**
200 * Notify that the peermap has been updated, that means a new peer
201 * has been created or some existing peer has been disposed.
202 *
203 * @see AWTAutoShutdown#isReadyToShutdown
204 */
205 void notifyPeerMapUpdated() {
206 synchronized (activationLock) {
207 synchronized (mainLock) {
208 if (!isReadyToShutdown() && blockerThread == null) {
209 activateBlockerThread();
210 } else {
211 mainLock.notifyAll();
212 timeoutPassed = false;
213 }
214 }
215 }
216 }
217
218 /**
219 * Determine whether AWT is currently in ready-to-shutdown state.
220 * AWT is considered to be in ready-to-shutdown state if
221 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>
222 * is false and <code>busyThreadSet</code> is empty.
223 *
224 * @return true if AWT is in ready-to-shutdown state.
225 */
226 private boolean isReadyToShutdown() {
227 return (!toolkitThreadBusy &&
228 peerMap.isEmpty() &&
229 busyThreadSet.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 * Creates and starts a new blocker thread. Doesn't return until
322 * the new blocker thread starts.
323 */
324 private void activateBlockerThread() {
325 Thread thread = new Thread(this, "AWT-Shutdown");
326 thread.setDaemon(false);
327 blockerThread = thread;
328 thread.start();
329 try {
330 /* Wait for the blocker thread to start. */
331 mainLock.wait();
332 } catch (InterruptedException e) {
333 System.err.println("AWT blocker activation interrupted:");
334 e.printStackTrace();
335 }
336 }
337
338 final void registerPeer(final Object target, final Object peer) {
339 synchronized (activationLock) {
340 synchronized (mainLock) {
341 peerMap.put(target, peer);
342 notifyPeerMapUpdated();
343 }
344 }
345 }
346
347 final void unregisterPeer(final Object target, final Object peer) {
348 synchronized (activationLock) {
349 synchronized (mainLock) {
350 if (peerMap.get(target) == peer) {
351 peerMap.remove(target);
352 notifyPeerMapUpdated();
353 }
354 }
355 }
356 }
357
358 final Object getPeer(final Object target) {
359 synchronized (activationLock) {
360 synchronized (mainLock) {
361 return peerMap.get(target);
362 }
363 }
364 }
365
366 final void dumpPeers(final Logger aLog) {
367 synchronized (activationLock) {
368 synchronized (mainLock) {
369 aLog.fine("Mapped peers:");
370 for (Object key : peerMap.keySet()) {
371 aLog.fine(key + "->" + peerMap.get(key));
372 }
373 }
374 }
375 }
376
377} // class AWTAutoShutdown