1 /*
2 * Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.awt;
27
28 import java.awt.AWTEvent;
29
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.IdentityHashMap;
33 import java.util.Map;
34 import java.util.Set;
35
36 import sun.util.logging.PlatformLogger;
37
38 /**
39 * This class is to let AWT shutdown automatically when a user is done
40 * with AWT. It tracks AWT state using the following parameters:
41 * <ul>
42 * <li><code>peerMap</code> - the map between the existing peer objects
43 * and their associated targets
44 * <li><code>toolkitThreadBusy</code> - whether the toolkit thread
45 * is waiting for a new native event to appear in its queue
46 * or is dispatching an event
47 * <li><code>busyThreadSet</code> - a set of all the event dispatch
48 * threads that are busy at this moment, i.e. those that are not
49 * 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 when
52 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>
53 * is false and <code>busyThreadSet</code> is empty.
54 * The internal AWTAutoShutdown logic secures that the single non-daemon
55 * thread (<code>blockerThread</code>) is running when AWT is not in
56 * ready-to-shutdown state. This blocker thread is to prevent AWT from
57 * exiting since the toolkit thread is now daemon and all the event
58 * dispatch threads are started only when needed. Once it is detected
59 * that AWT is in ready-to-shutdown state this blocker thread waits
60 * for a certain timeout and if AWT state doesn't change during timeout
61 * this blocker thread terminates all the event dispatch threads and
62 * exits.
63 */
64 public final class AWTAutoShutdown implements Runnable {
65
66 private static final AWTAutoShutdown theInstance = new AWTAutoShutdown();
67
68 /**
69 * This lock object is used to synchronize shutdown operations.
70 */
71 private final Object mainLock = new Object();
72
73 /**
74 * This lock object is to secure that when a new blocker thread is
75 * started it will be the first who acquire the main lock after
76 * the thread that created the new blocker released the main lock
77 * by calling lock.wait() to wait for the blocker to start.
78 */
79 private final Object activationLock = new Object();
80
81 /**
82 * This set keeps references to all the event dispatch threads that
83 * are busy at this moment, i.e. those that are not waiting for a
84 * new event to appear in their event queue.
85 * Access is synchronized on the main lock object.
86 */
87 private final Set<Thread> busyThreadSet = new HashSet<>(7);
88
89 /**
90 * Indicates whether the toolkit thread is waiting for a new native
91 * event to appear or is dispatching an event.
92 */
93 private boolean toolkitThreadBusy = false;
94
95 /**
96 * This is a map between components and their peers.
97 * we should work with in under activationLock&mainLock lock.
98 */
99 private final Map<Object, Object> peerMap = new IdentityHashMap<>();
100
101 /**
102 * References the alive non-daemon thread that is currently used
103 * for keeping AWT from exiting.
104 */
105 private Thread blockerThread = null;
106
107 /**
108 * We need this flag to secure that AWT state hasn't changed while
109 * we were waiting for the safety timeout to pass.
110 */
111 private boolean timeoutPassed = false;
112
113 /**
114 * Once we detect that AWT is ready to shutdown we wait for a certain
115 * timeout to pass before stopping event dispatch threads.
116 */
117 private static final int SAFETY_TIMEOUT = 1000;
118
119 /**
120 * Constructor method is intentionally made private to secure
121 * a single instance. Use getInstance() to reference it.
122 *
123 * @see AWTAutoShutdown#getInstance
124 */
125 private AWTAutoShutdown() {}
126
127 /**
128 * Returns reference to a single AWTAutoShutdown instance.
129 */
130 public static AWTAutoShutdown getInstance() {
131 return theInstance;
132 }
133
134 /**
135 * Notify that the toolkit thread is not waiting for a native event
136 * to appear in its queue.
137 *
138 * @see AWTAutoShutdown#notifyToolkitThreadFree
139 * @see AWTAutoShutdown#setToolkitBusy
140 * @see AWTAutoShutdown#isReadyToShutdown
141 */
142 public static void notifyToolkitThreadBusy() {
143 getInstance().setToolkitBusy(true);
144 }
145
146 /**
147 * Notify that the toolkit thread is waiting for a native event
148 * to appear in its queue.
149 *
150 * @see AWTAutoShutdown#notifyToolkitThreadFree
151 * @see AWTAutoShutdown#setToolkitBusy
152 * @see AWTAutoShutdown#isReadyToShutdown
153 */
154 public static void notifyToolkitThreadFree() {
155 getInstance().setToolkitBusy(false);
156 }
157
158 /**
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#notifyThreadFree
165 * @see AWTAutoShutdown#isReadyToShutdown
166 */
167 public void notifyThreadBusy(final Thread thread) {
168 if (thread == null) {
169 return;
170 }
171 synchronized (activationLock) {
172 synchronized (mainLock) {
173 if (blockerThread == null) {
174 activateBlockerThread();
175 } else if (isReadyToShutdown()) {
176 mainLock.notifyAll();
177 timeoutPassed = false;
178 }
179 busyThreadSet.add(thread);
180 }
181 }
182 }
183
184 /**
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#notifyThreadBusy
191 * @see AWTAutoShutdown#isReadyToShutdown
192 */
193 public void notifyThreadFree(final Thread thread) {
194 if (thread == null) {
195 return;
196 }
197 synchronized (activationLock) {
198 synchronized (mainLock) {
199 busyThreadSet.remove(thread);
200 if (isReadyToShutdown()) {
201 mainLock.notifyAll();
202 timeoutPassed = false;
203 }
204 }
205 }
206 }
207
208 /**
209 * Notify that the peermap has been updated, that means a new peer
210 * has been created or some existing peer has been disposed.
211 *
212 * @see AWTAutoShutdown#isReadyToShutdown
213 */
214 void notifyPeerMapUpdated() {
215 synchronized (activationLock) {
216 synchronized (mainLock) {
217 if (!isReadyToShutdown() && blockerThread == null) {
218 activateBlockerThread();
219 } else {
220 mainLock.notifyAll();
221 timeoutPassed = false;
222 }
223 }
224 }
225 }
226
227 /**
228 * Determine whether AWT is currently in ready-to-shutdown state.
229 * AWT is considered to be in ready-to-shutdown state if
230 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code>
231 * is false and <code>busyThreadSet</code> is empty.
232 *
233 * @return true if AWT is in ready-to-shutdown state.
234 */
235 private boolean isReadyToShutdown() {
236 return (!toolkitThreadBusy &&
237 peerMap.isEmpty() &&
238 busyThreadSet.isEmpty());
239 }
240
241 /**
242 * Notify about the toolkit thread state change.
243 *
244 * @param busy true if the toolkit thread state changes from idle
245 * to busy.
246 * @see AWTAutoShutdown#notifyToolkitThreadBusy
247 * @see AWTAutoShutdown#notifyToolkitThreadFree
248 * @see AWTAutoShutdown#isReadyToShutdown
249 */
250 private void setToolkitBusy(final boolean busy) {
251 if (busy != toolkitThreadBusy) {
252 synchronized (activationLock) {
253 synchronized (mainLock) {
254 if (busy != toolkitThreadBusy) {
255 if (busy) {
256 if (blockerThread == null) {
257 activateBlockerThread();
258 } else if (isReadyToShutdown()) {
259 mainLock.notifyAll();
260 timeoutPassed = false;
261 }
262 toolkitThreadBusy = busy;
263 } else {
264 toolkitThreadBusy = busy;
265 if (isReadyToShutdown()) {
266 mainLock.notifyAll();
267 timeoutPassed = false;
268 }
269 }
270 }
271 }
272 }
273 }
274 }
275
276 /**
277 * Implementation of the Runnable interface.
278 * Incapsulates the blocker thread functionality.
279 *
280 * @see AWTAutoShutdown#isReadyToShutdown
281 */
282 public void run() {
283 Thread currentThread = Thread.currentThread();
284 boolean interrupted = false;
285 synchronized (mainLock) {
286 try {
287 /* Notify that the thread is started. */
288 mainLock.notifyAll();
289 while (blockerThread == currentThread) {
290 mainLock.wait();
291 timeoutPassed = false;
292 /*
293 * This loop is introduced to handle the following case:
294 * it is possible that while we are waiting for the
295 * safety timeout to pass AWT state can change to
296 * 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 loop
299 * in this case, since we may never be notified
300 * in an outer infinite wait at this point.
301 */
302 while (isReadyToShutdown()) {
303 if (timeoutPassed) {
304 timeoutPassed = false;
305 blockerThread = null;
306 break;
307 }
308 timeoutPassed = true;
309 mainLock.wait(SAFETY_TIMEOUT);
310 }
311 }
312 } catch (InterruptedException e) {
313 interrupted = true;
314 } finally {
315 if (blockerThread == currentThread) {
316 blockerThread = null;
317 }
318 }
319 }
320 if (!interrupted) {
321 AppContext.stopEventDispatchThreads();
322 }
323 }
324
325 @SuppressWarnings("serial")
326 static AWTEvent getShutdownEvent() {
327 return new AWTEvent(getInstance(), 0) {
328 };
329 }
330
331 /**
332 * Creates and starts a new blocker thread. Doesn't return until
333 * the new blocker thread starts.
334 */
335 private void activateBlockerThread() {
336 Thread thread = new Thread(this, "AWT-Shutdown");
337 thread.setDaemon(false);
338 blockerThread = thread;
339 thread.start();
340 try {
341 /* Wait for the blocker thread to start. */
342 mainLock.wait();
343 } catch (InterruptedException e) {
344 System.err.println("AWT blocker activation interrupted:");
345 e.printStackTrace();
346 }
347 }
348
349 final void registerPeer(final Object target, final Object peer) {
350 synchronized (activationLock) {
351 synchronized (mainLock) {
352 peerMap.put(target, peer);
353 notifyPeerMapUpdated();
354 }
355 }
356 }
357
358 final void unregisterPeer(final Object target, final Object peer) {
359 synchronized (activationLock) {
360 synchronized (mainLock) {
361 if (peerMap.get(target) == peer) {
362 peerMap.remove(target);
363 notifyPeerMapUpdated();
364 }
365 }
366 }
367 }
368
369 final Object getPeer(final Object target) {
370 synchronized (activationLock) {
371 synchronized (mainLock) {
372 return peerMap.get(target);
373 }
374 }
375 }
376
377 final void dumpPeers(final PlatformLogger aLog) {
378 if (aLog.isLoggable(PlatformLogger.FINE)) {
379 synchronized (activationLock) {
380 synchronized (mainLock) {
381 aLog.fine("Mapped peers:");
382 for (Object key : peerMap.keySet()) {
383 aLog.fine(key + "->" + peerMap.get(key));
384 }
385 }
386 }
387 }
388 }
389
390 } // class AWTAutoShutdown
--- EOF ---