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