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 java.awt;
27
28 import java.security.AccessController;
29 import java.security.PrivilegedAction;
30 import java.util.LinkedList;
31 import sun.awt.AWTAccessor;
32 import sun.awt.AppContext;
33 import sun.awt.SunToolkit;
34
35 /**
36 * A mechanism for ensuring that a series of AWTEvents are executed in a
37 * precise order, even across multiple AppContexts. The nested events will be
38 * dispatched in the order in which their wrapping SequencedEvents were
39 * constructed. The only exception to this rule is if the peer of the target of
40 * the nested event was destroyed (with a call to Component.removeNotify)
41 * before the wrapping SequencedEvent was able to be dispatched. In this case,
42 * the nested event is never dispatched.
43 *
44 * @author David Mendenhall
45 */
46 class SequencedEvent extends AWTEvent implements ActiveEvent {
47 /*
48 * serialVersionUID
49 */
50 private static final long serialVersionUID = 547742659238625067L;
51
52 private static final int ID =
53 java.awt.event.FocusEvent.FOCUS_LAST + 1;
54 private static final LinkedList<SequencedEvent> list = new LinkedList<>();
55
56 private final AWTEvent nested;
57 private AppContext appContext;
58 private boolean disposed;
59
60 private static boolean fxAppThreadIsDispatchThread;
61 private Thread fxCheckSequenceThread;
62 static {
63 AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() {
64 public AWTEvent getNested(AWTEvent sequencedEvent) {
65 return ((SequencedEvent)sequencedEvent).nested;
66 }
67 public boolean isSequencedEvent(AWTEvent event) {
68 return event instanceof SequencedEvent;
69 }
70
71 public AWTEvent create(AWTEvent event) {
72 return new SequencedEvent(event);
73 }
74 });
75 AccessController.doPrivileged(new PrivilegedAction<Object>() {
76 public Object run() {
77 fxAppThreadIsDispatchThread =
78 "true".equals(System.getProperty("javafx.embed.singleThread"));
79 return null;
80 }
81 });
82 }
83
84 /**
85 * Constructs a new SequencedEvent which will dispatch the specified
86 * nested event.
87 *
88 * @param nested the AWTEvent which this SequencedEvent's dispatch()
89 * method will dispatch
90 */
91 public SequencedEvent(AWTEvent nested) {
92 super(nested.getSource(), ID);
93 this.nested = nested;
94 // All AWTEvents that are wrapped in SequencedEvents are (at
95 // least currently) implicitly generated by the system
96 SunToolkit.setSystemGenerated(nested);
97
98 if (fxAppThreadIsDispatchThread) {
99 fxCheckSequenceThread = new Thread() {
100 @Override
101 public void run() {
102 while(!isFirstOrDisposed()) {
103 try {
104 Thread.sleep(100);
105 } catch (InterruptedException e) {
106 break;
107 }
108 }
109 }
110 };
111 }
112 synchronized (SequencedEvent.class) {
113 list.add(this);
114 }
115 }
116
117 /**
118 * Dispatches the nested event after all previous nested events have been
119 * dispatched or disposed. If this method is invoked before all previous nested events
120 * have been dispatched, then this method blocks until such a point is
121 * reached.
122 * While waiting disposes nested events to disposed AppContext
123 *
124 * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
125 * dispatch() shall never call dispose() while holding the lock on the list,
126 * as EventQueue lock is held during dispatching. The locks should be acquired
127 * in the same order.
128 */
129 public final void dispatch() {
130 try {
131 appContext = AppContext.getAppContext();
132
133 if (getFirst() != this) {
134 if (EventQueue.isDispatchThread()) {
135 if (Thread.currentThread() instanceof EventDispatchThread) {
136 EventDispatchThread edt = (EventDispatchThread)
137 Thread.currentThread();
138 edt.pumpEvents(ID, () -> !SequencedEvent.this.isFirstOrDisposed());
139 } else {
140 if (fxAppThreadIsDispatchThread) {
141 fxCheckSequenceThread.start();
142 try {
143 // check if event is dispatched or disposed
144 // but since this user app thread is same as
145 // dispatch thread in fx when run with
146 // javafx.embed.singleThread=true
147 // we do not wait infinitely to avoid deadlock
148 // as dispatch will ultimately be done by this thread
149 fxCheckSequenceThread.join(500);
150 } catch (InterruptedException e) {
151 }
152 }
153 }
154 } else {
155 while(!isFirstOrDisposed()) {
156 synchronized (SequencedEvent.class) {
157 try {
158 SequencedEvent.class.wait(1000);
159 } catch (InterruptedException e) {
160 break;
161 }
162 }
163 }
164 }
165 }
166
167 if (!disposed) {
168 KeyboardFocusManager.getCurrentKeyboardFocusManager().
169 setCurrentSequencedEvent(this);
170 Toolkit.getEventQueue().dispatchEvent(nested);
171 }
172 } finally {
173 dispose();
174 }
175 }
176
177 /**
178 * true only if event exists and nested source appContext is disposed.
179 */
180 private static final boolean isOwnerAppContextDisposed(SequencedEvent se) {
181 if (se != null) {
182 Object target = se.nested.getSource();
183 if (target instanceof Component) {
184 return ((Component)target).appContext.isDisposed();
185 }
186 }
187 return false;
188 }
189
190 /**
191 * Sequenced events are dispatched in order, so we cannot dispatch
192 * until we are the first sequenced event in the queue (i.e. it's our
193 * turn). But while we wait for our turn to dispatch, the event
194 * could have been disposed for a number of reasons.
195 */
196 public final boolean isFirstOrDisposed() {
197 if (disposed) {
198 return true;
199 }
200 // getFirstWithContext can dispose this
201 return this == getFirstWithContext() || disposed;
202 }
203
204 private static final synchronized SequencedEvent getFirst() {
205 return list.getFirst();
206 }
207
208 /* Disposes all events from disposed AppContext
209 * return first valid event
210 */
211 private static final SequencedEvent getFirstWithContext() {
212 SequencedEvent first = getFirst();
213 while(isOwnerAppContextDisposed(first)) {
214 first.dispose();
215 first = getFirst();
216 }
217 return first;
218 }
219
220 /**
221 * Disposes of this instance. This method is invoked once the nested event
222 * has been dispatched and handled, or when the peer of the target of the
223 * nested event has been disposed with a call to Component.removeNotify.
224 *
225 * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock,
226 * it shall never be called while holding the lock on the list,
227 * as EventQueue lock is held during dispatching and dispatch() will get
228 * lock on the list. The locks should be acquired in the same order.
229 */
230 final void dispose() {
231 synchronized (SequencedEvent.class) {
232 if (disposed) {
233 return;
234 }
235 if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
236 getCurrentSequencedEvent() == this) {
237 KeyboardFocusManager.getCurrentKeyboardFocusManager().
238 setCurrentSequencedEvent(null);
239 }
240 disposed = true;
241 }
242 // Wake myself up
243 if (appContext != null) {
244 SunToolkit.postEvent(appContext, new SentEvent());
245 }
246
247 SequencedEvent next = null;
248
249 synchronized (SequencedEvent.class) {
250 SequencedEvent.class.notifyAll();
251
252 if (list.getFirst() == this) {
253 list.removeFirst();
254
255 if (!list.isEmpty()) {
256 next = list.getFirst();
257 }
258 } else {
259 list.remove(this);
260 }
261 }
262 // Wake up waiting threads
263 if (next != null && next.appContext != null) {
264 SunToolkit.postEvent(next.appContext, new SentEvent());
265 }
266 }
267 }
--- EOF ---