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 currentSequencedEvent.pendingEvents.add(ev);
108 return FilterAction.REJECT;
109 }
110 return FilterAction.ACCEPT;
111 }
112 }
113
114 /**
115 * Constructs a new SequencedEvent which will dispatch the specified
116 * nested event.
117 *
118 * @param nested the AWTEvent which this SequencedEvent's dispatch()
119 * method will dispatch
120 */
121 public SequencedEvent(AWTEvent nested) {
122 super(nested.getSource(), ID);
123 this.nested = nested;
124 // All AWTEvents that are wrapped in SequencedEvents are (at
125 // least currently) implicitly generated by the system
126 SunToolkit.setSystemGenerated(nested);
127
128 if (fxAppThreadIsDispatchThread) {
129 fxCheckSequenceThread = new Thread() {
130 @Override
131 public void run() {
132 while(!isFirstOrDisposed()) {
133 try {
148 * Dispatches the nested event after all previous nested events have been
149 * dispatched or disposed. If this method is invoked before all previous nested events
150 * have been dispatched, then this method blocks until such a point is
151 * reached.
152 * While waiting disposes nested events to disposed AppContext
153 *
154 * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
155 * dispatch() shall never call dispose() while holding the lock on the list,
156 * as EventQueue lock is held during dispatching. The locks should be acquired
157 * in the same order.
158 */
159 public final void dispatch() {
160 try {
161 appContext = AppContext.getAppContext();
162
163 if (getFirst() != this) {
164 if (EventQueue.isDispatchThread()) {
165 if (Thread.currentThread() instanceof EventDispatchThread) {
166 EventDispatchThread edt = (EventDispatchThread)
167 Thread.currentThread();
168 edt.pumpEventsForFilter(() -> !SequencedEvent.this.isFirstOrDisposed(),
169 new SequencedEventsFilter(this));
170 } else {
171 if (fxAppThreadIsDispatchThread) {
172 fxCheckSequenceThread.start();
173 try {
174 // check if event is dispatched or disposed
175 // but since this user app thread is same as
176 // dispatch thread in fx when run with
177 // javafx.embed.singleThread=true
178 // we do not wait infinitely to avoid deadlock
179 // as dispatch will ultimately be done by this thread
180 fxCheckSequenceThread.join(500);
181 } catch (InterruptedException e) {
182 }
183 }
184 }
185 } else {
186 while(!isFirstOrDisposed()) {
187 synchronized (SequencedEvent.class) {
188 try {
189 SequencedEvent.class.wait(1000);
253 * has been dispatched and handled, or when the peer of the target of the
254 * nested event has been disposed with a call to Component.removeNotify.
255 *
256 * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock,
257 * it shall never be called while holding the lock on the list,
258 * as EventQueue lock is held during dispatching and dispatch() will get
259 * lock on the list. The locks should be acquired in the same order.
260 */
261 final void dispose() {
262 synchronized (SequencedEvent.class) {
263 if (disposed) {
264 return;
265 }
266 if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
267 getCurrentSequencedEvent() == this) {
268 KeyboardFocusManager.getCurrentKeyboardFocusManager().
269 setCurrentSequencedEvent(null);
270 }
271 disposed = true;
272 }
273
274 SequencedEvent next = null;
275
276 synchronized (SequencedEvent.class) {
277 SequencedEvent.class.notifyAll();
278
279 if (list.getFirst() == this) {
280 list.removeFirst();
281
282 if (!list.isEmpty()) {
283 next = list.getFirst();
284 }
285 } else {
286 list.remove(this);
287 }
288 }
289 // Wake up waiting threads
290 if (next != null && next.appContext != null) {
291 SunToolkit.postEvent(next.appContext, new SentEvent());
292 }
293
294 for(AWTEvent e : pendingEvents) {
295 SunToolkit.postEvent(appContext, e);
296 }
297 }
298 }
|