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