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 {
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);
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 }
|
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.Iterator;
31 import java.util.LinkedList;
32 import sun.awt.AWTAccessor;
33 import sun.awt.AppContext;
34 import sun.awt.SunToolkit;
35
36 /**
37 * A mechanism for ensuring that a series of AWTEvents are executed in a
38 * precise order, even across multiple AppContexts. The nested events will be
39 * dispatched in the order in which their wrapping SequencedEvents were
40 * constructed. The only exception to this rule is if the peer of the target of
41 * the nested event was destroyed (with a call to Component.removeNotify)
42 * before the wrapping SequencedEvent was able to be dispatched. In this case,
43 * the nested event is never dispatched.
44 *
45 * @author David Mendenhall
46 */
47 class SequencedEvent extends AWTEvent implements ActiveEvent {
48 /*
49 * serialVersionUID
50 */
51 private static final long serialVersionUID = 547742659238625067L;
52
53 private static final int ID =
54 java.awt.event.FocusEvent.FOCUS_LAST + 1;
55 private static final LinkedList<SequencedEvent> list = new LinkedList<>();
56
57 private final AWTEvent nested;
58 private AppContext appContext;
59 private boolean disposed;
60 private final LinkedList<AWTEvent> pendingEvents = new LinkedList<>();
61
62 private static boolean fxAppThreadIsDispatchThread;
63 private Thread fxCheckSequenceThread;
64 static {
65 AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() {
66 public AWTEvent getNested(AWTEvent sequencedEvent) {
67 return ((SequencedEvent)sequencedEvent).nested;
68 }
69 public boolean isSequencedEvent(AWTEvent event) {
70 return event instanceof SequencedEvent;
71 }
72
73 public AWTEvent create(AWTEvent event) {
74 return new SequencedEvent(event);
75 }
76 });
77 AccessController.doPrivileged(new PrivilegedAction<Object>() {
78 public Object run() {
79 fxAppThreadIsDispatchThread =
80 "true".equals(System.getProperty("javafx.embed.singleThread"));
81 return null;
82 }
83 });
84 }
85
86 private static final class SequencedEventsFilter implements EventFilter {
87 private final SequencedEvent currentSequencedEvent;
88 private SequencedEventsFilter(SequencedEvent currentSequencedEvent) {
89 this.currentSequencedEvent = currentSequencedEvent;
90 }
91 @Override
92 public FilterAction acceptEvent(AWTEvent ev) {
93 if (ev.getID() == ID) {
94 // Move forward dispatching only if the event is previous
95 // in SequencedEvent.list. Otherwise, hold it for reposting later.
96 synchronized (SequencedEvent.class) {
97 Iterator<SequencedEvent> it = list.iterator();
98 while (it.hasNext()) {
99 SequencedEvent iev = it.next();
100 if (iev.equals(currentSequencedEvent)) {
101 break;
102 } else if (iev.equals(ev)) {
103 return FilterAction.ACCEPT;
104 }
105 }
106 }
107 } else if (ev.getID() == SentEvent.ID) {
108 return FilterAction.ACCEPT;
109 }
110 currentSequencedEvent.pendingEvents.add(ev);
111 return FilterAction.REJECT;
112 }
113 }
114
115 /**
116 * Constructs a new SequencedEvent which will dispatch the specified
117 * nested event.
118 *
119 * @param nested the AWTEvent which this SequencedEvent's dispatch()
120 * method will dispatch
121 */
122 public SequencedEvent(AWTEvent nested) {
123 super(nested.getSource(), ID);
124 this.nested = nested;
125 // All AWTEvents that are wrapped in SequencedEvents are (at
126 // least currently) implicitly generated by the system
127 SunToolkit.setSystemGenerated(nested);
128
129 if (fxAppThreadIsDispatchThread) {
130 fxCheckSequenceThread = new Thread() {
131 @Override
132 public void run() {
133 while(!isFirstOrDisposed()) {
134 try {
149 * Dispatches the nested event after all previous nested events have been
150 * dispatched or disposed. If this method is invoked before all previous nested events
151 * have been dispatched, then this method blocks until such a point is
152 * reached.
153 * While waiting disposes nested events to disposed AppContext
154 *
155 * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
156 * dispatch() shall never call dispose() while holding the lock on the list,
157 * as EventQueue lock is held during dispatching. The locks should be acquired
158 * in the same order.
159 */
160 public final void dispatch() {
161 try {
162 appContext = AppContext.getAppContext();
163
164 if (getFirst() != this) {
165 if (EventQueue.isDispatchThread()) {
166 if (Thread.currentThread() instanceof EventDispatchThread) {
167 EventDispatchThread edt = (EventDispatchThread)
168 Thread.currentThread();
169 edt.pumpEventsForFilter(() -> !SequencedEvent.this.isFirstOrDisposed(),
170 new SequencedEventsFilter(this));
171 } else {
172 if (fxAppThreadIsDispatchThread) {
173 fxCheckSequenceThread.start();
174 try {
175 // check if event is dispatched or disposed
176 // but since this user app thread is same as
177 // dispatch thread in fx when run with
178 // javafx.embed.singleThread=true
179 // we do not wait infinitely to avoid deadlock
180 // as dispatch will ultimately be done by this thread
181 fxCheckSequenceThread.join(500);
182 } catch (InterruptedException e) {
183 }
184 }
185 }
186 } else {
187 while(!isFirstOrDisposed()) {
188 synchronized (SequencedEvent.class) {
189 try {
190 SequencedEvent.class.wait(1000);
254 * has been dispatched and handled, or when the peer of the target of the
255 * nested event has been disposed with a call to Component.removeNotify.
256 *
257 * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock,
258 * it shall never be called while holding the lock on the list,
259 * as EventQueue lock is held during dispatching and dispatch() will get
260 * lock on the list. The locks should be acquired in the same order.
261 */
262 final void dispose() {
263 synchronized (SequencedEvent.class) {
264 if (disposed) {
265 return;
266 }
267 if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
268 getCurrentSequencedEvent() == this) {
269 KeyboardFocusManager.getCurrentKeyboardFocusManager().
270 setCurrentSequencedEvent(null);
271 }
272 disposed = true;
273 }
274
275 SequencedEvent next = null;
276
277 synchronized (SequencedEvent.class) {
278 SequencedEvent.class.notifyAll();
279
280 if (list.getFirst() == this) {
281 list.removeFirst();
282
283 if (!list.isEmpty()) {
284 next = list.getFirst();
285 }
286 } else {
287 list.remove(this);
288 }
289 }
290 // Wake up waiting threads
291 if (next != null && next.appContext != null) {
292 SunToolkit.postEvent(next.appContext, new SentEvent());
293 }
294
295 for(AWTEvent e : pendingEvents) {
296 SunToolkit.postEvent(appContext, e);
297 }
298 }
299 }
|