8088132: [Swing, singleThread] ClassCastException in nested event loop when showing multiple message dialogs in SwingNode
authorpsadhukhan
Tue, 26 Sep 2017 10:46:23 +0530
changeset 47374 bf712ea57bb0
parent 47373 791e7f39b45a
child 47375 378efa95df46
8088132: [Swing, singleThread] ClassCastException in nested event loop when showing multiple message dialogs in SwingNode Reviewed-by: serb
src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java
src/java.desktop/share/classes/java/awt/SequencedEvent.java
--- a/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java	Mon Sep 25 16:12:49 2017 +0530
+++ b/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java	Tue Sep 26 10:46:23 2017 +0530
@@ -30,6 +30,8 @@
 import java.awt.peer.ComponentPeer;
 import java.awt.peer.LightweightPeer;
 import java.lang.ref.WeakReference;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.LinkedList;
 import java.util.Iterator;
 import java.util.ListIterator;
@@ -77,6 +79,8 @@
     private boolean consumeNextKeyTyped;
     private Component restoreFocusTo;
 
+    private static boolean fxAppThreadIsDispatchThread;
+
     static {
         AWTAccessor.setDefaultKeyboardFocusManagerAccessor(
             new AWTAccessor.DefaultKeyboardFocusManagerAccessor() {
@@ -84,6 +88,13 @@
                     dkfm.consumeNextKeyTyped(e);
                 }
             });
+        AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            public Object run() {
+                fxAppThreadIsDispatchThread =
+                        "true".equals(System.getProperty("javafx.embed.singleThread"));
+                return null;
+            }
+        });
     }
 
     private static class TypeAheadMarker {
@@ -264,13 +275,41 @@
             }
             SunToolkit.postEvent(targetAppContext, se);
             if (EventQueue.isDispatchThread()) {
-                EventDispatchThread edt = (EventDispatchThread)
-                    Thread.currentThread();
-                edt.pumpEvents(SentEvent.ID, new Conditional() {
+                if (Thread.currentThread() instanceof EventDispatchThread) {
+                    EventDispatchThread edt = (EventDispatchThread)
+                            Thread.currentThread();
+                    edt.pumpEvents(SentEvent.ID, new Conditional() {
                         public boolean evaluate() {
                             return !se.dispatched && !targetAppContext.isDisposed();
                         }
                     });
+                } else {
+                    if (fxAppThreadIsDispatchThread) {
+                        Thread fxCheckDispatchThread = new Thread() {
+                            @Override
+                            public void run() {
+                                while (!se.dispatched && !targetAppContext.isDisposed()) {
+                                    try {
+                                        Thread.sleep(100);
+                                    } catch (InterruptedException e) {
+                                        break;
+                                    }
+                                }
+                            }
+                        };
+                        fxCheckDispatchThread.start();
+                        try {
+                            // check if event is dispatched or disposed
+                            // but since this user app thread is same as
+                            // dispatch thread in fx when run with
+                            // javafx.embed.singleThread=true
+                            // we do not wait infinitely to avoid deadlock
+                            // as dispatch will ultimately be done by this thread
+                            fxCheckDispatchThread.join(500);
+                        } catch (InterruptedException ex) {
+                        }
+                    }
+                }
             } else {
                 synchronized (se) {
                     while (!se.dispatched && !targetAppContext.isDisposed()) {
--- a/src/java.desktop/share/classes/java/awt/SequencedEvent.java	Mon Sep 25 16:12:49 2017 +0530
+++ b/src/java.desktop/share/classes/java/awt/SequencedEvent.java	Tue Sep 26 10:46:23 2017 +0530
@@ -25,6 +25,8 @@
 
 package java.awt;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.LinkedList;
 import sun.awt.AWTAccessor;
 import sun.awt.AppContext;
@@ -55,6 +57,8 @@
     private AppContext appContext;
     private boolean disposed;
 
+    private static boolean fxAppThreadIsDispatchThread;
+    private Thread fxCheckSequenceThread;
     static {
         AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() {
             public AWTEvent getNested(AWTEvent sequencedEvent) {
@@ -68,6 +72,13 @@
                 return new SequencedEvent(event);
             }
         });
+        AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            public Object run() {
+                fxAppThreadIsDispatchThread =
+                        "true".equals(System.getProperty("javafx.embed.singleThread"));
+                return null;
+            }
+        });
     }
 
     /**
@@ -83,6 +94,21 @@
         // All AWTEvents that are wrapped in SequencedEvents are (at
         // least currently) implicitly generated by the system
         SunToolkit.setSystemGenerated(nested);
+
+        if (fxAppThreadIsDispatchThread) {
+            fxCheckSequenceThread = new Thread() {
+                @Override
+                public void run() {
+                    while(!isFirstOrDisposed()) {
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {
+                            break;
+                        }
+                    }
+                }
+            };
+        }
         synchronized (SequencedEvent.class) {
             list.add(this);
         }
@@ -106,13 +132,29 @@
 
             if (getFirst() != this) {
                 if (EventQueue.isDispatchThread()) {
-                    EventDispatchThread edt = (EventDispatchThread)
-                        Thread.currentThread();
-                    edt.pumpEvents(SentEvent.ID, new Conditional() {
-                        public boolean evaluate() {
-                            return !SequencedEvent.this.isFirstOrDisposed();
+                    if (Thread.currentThread() instanceof EventDispatchThread) {
+                        EventDispatchThread edt = (EventDispatchThread)
+                                Thread.currentThread();
+                        edt.pumpEvents(SentEvent.ID, new Conditional() {
+                            public boolean evaluate() {
+                                return !SequencedEvent.this.isFirstOrDisposed();
+                            }
+                        });
+                    } else {
+                        if (fxAppThreadIsDispatchThread) {
+                            fxCheckSequenceThread.start();
+                            try {
+                                // check if event is dispatched or disposed
+                                // but since this user app thread is same as
+                                // dispatch thread in fx when run with
+                                // javafx.embed.singleThread=true
+                                // we do not wait infinitely to avoid deadlock
+                                // as dispatch will ultimately be done by this thread
+                                fxCheckSequenceThread.join(500);
+                            } catch (InterruptedException e) {
+                            }
                         }
-                    });
+                    }
                 } else {
                     while(!isFirstOrDisposed()) {
                         synchronized (SequencedEvent.class) {