8025588: [macosx] Frozen AppKit thread in 7u40
authorpchelko
Thu, 10 Oct 2013 11:40:06 +0400
changeset 21246 9d1752954b0e
parent 21245 786207514920
child 21247 40e9d768d8e2
8025588: [macosx] Frozen AppKit thread in 7u40 Reviewed-by: anthony, art, serb
jdk/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
jdk/src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java
jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
jdk/src/share/classes/java/awt/EventQueue.java
jdk/src/share/classes/java/awt/event/InvocationEvent.java
jdk/src/share/classes/sun/awt/AWTAccessor.java
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java	Thu Oct 10 11:40:06 2013 +0400
@@ -620,8 +620,7 @@
                     retString[0] = new String(selectedText);
                 }}
             }, fAwtFocussedComponent);
-        } catch (InterruptedException ie) { ie.printStackTrace(); }
-        catch (InvocationTargetException ite) { ite.printStackTrace(); }
+        } catch (InvocationTargetException ite) { ite.printStackTrace(); }
 
         synchronized(retString) { return retString[0]; }
     }
@@ -669,8 +668,7 @@
 
                 }}
             }, fAwtFocussedComponent);
-        } catch (InterruptedException ie) { ie.printStackTrace(); }
-        catch (InvocationTargetException ite) { ite.printStackTrace(); }
+        } catch (InvocationTargetException ite) { ite.printStackTrace(); }
 
         synchronized(returnValue) { return returnValue; }
     }
@@ -695,8 +693,7 @@
                     returnValue[0] = fIMContext.getInsertPositionOffset();
                 }}
             }, fAwtFocussedComponent);
-        } catch (InterruptedException ie) { ie.printStackTrace(); }
-        catch (InvocationTargetException ite) { ite.printStackTrace(); }
+        } catch (InvocationTargetException ite) { ite.printStackTrace(); }
 
         returnValue[1] = fCurrentTextLength;
         synchronized(returnValue) { return returnValue; }
@@ -743,8 +740,7 @@
                     }
                 }}
             }, fAwtFocussedComponent);
-        } catch (InterruptedException ie) { ie.printStackTrace(); }
-        catch (InvocationTargetException ite) { ite.printStackTrace(); }
+        } catch (InvocationTargetException ite) { ite.printStackTrace(); }
 
         synchronized(rect) { return rect; }
     }
@@ -764,8 +760,7 @@
                     insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
                 }}
             }, fAwtFocussedComponent);
-        } catch (InterruptedException ie) { ie.printStackTrace(); }
-        catch (InvocationTargetException ite) { ite.printStackTrace(); }
+        } catch (InvocationTargetException ite) { ite.printStackTrace(); }
 
         // This bit of gymnastics ensures that the returned location is within the composed text.
         // If it falls outside that region, the input method will commit the text, which is inconsistent with native
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Oct 10 11:40:06 2013 +0400
@@ -884,7 +884,7 @@
                         //Posting an empty to flush the EventQueue without blocking the main thread
                     }
                 }, target);
-            } catch (InterruptedException | InvocationTargetException e) {
+            } catch (InvocationTargetException e) {
                 e.printStackTrace();
             }
         }
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java	Thu Oct 10 11:40:06 2013 +0400
@@ -97,6 +97,6 @@
                     setVisible(true);
                 }
             }, this);
-        } catch (InterruptedException | InvocationTargetException ex) {}
+        } catch (InvocationTargetException ex) {}
     }
 }
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java	Thu Oct 10 11:40:06 2013 +0400
@@ -548,22 +548,18 @@
     // Any selector invoked using ThreadUtilities performOnMainThread will be processed in doAWTRunLoop
     // The InvocationEvent will call LWCToolkit.stopAWTRunLoop() when finished, which will stop our manual runloop
     // Does not dispatch native events while in the loop
-    public static void invokeAndWait(Runnable event, Component component) throws InterruptedException, InvocationTargetException {
+    public static void invokeAndWait(Runnable runnable, Component component) throws InvocationTargetException {
         final long mediator = createAWTRunLoopMediator();
 
         InvocationEvent invocationEvent =
-                new InvocationEvent(component != null ? component : Toolkit.getDefaultToolkit(), event) {
-                    @Override
-                    public void dispatch() {
-                        try {
-                            super.dispatch();
-                        } finally {
+                new InvocationEvent(component != null ? component : Toolkit.getDefaultToolkit(),
+                        runnable,
+                        () -> {
                             if (mediator != 0) {
                                 stopAWTRunLoop(mediator);
                             }
-                        }
-                    }
-                };
+                        },
+                        true);
 
         if (component != null) {
             AppContext appContext = SunToolkit.targetToAppContext(component);
--- a/jdk/src/share/classes/java/awt/EventQueue.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/share/classes/java/awt/EventQueue.java	Thu Oct 10 11:40:06 2013 +0400
@@ -1159,6 +1159,10 @@
                         if (entry.event instanceof SentEvent) {
                             ((SentEvent)entry.event).dispose();
                         }
+                        if (entry.event instanceof InvocationEvent) {
+                            AWTAccessor.getInvocationEventAccessor()
+                                    .dispose((InvocationEvent)entry.event);
+                        }
                         if (prev == null) {
                             queues[i].head = entry.next;
                         } else {
--- a/jdk/src/share/classes/java/awt/event/InvocationEvent.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/share/classes/java/awt/event/InvocationEvent.java	Thu Oct 10 11:40:06 2013 +0400
@@ -25,6 +25,8 @@
 
 package java.awt.event;
 
+import sun.awt.AWTAccessor;
+
 import java.awt.ActiveEvent;
 import java.awt.AWTEvent;
 
@@ -56,6 +58,15 @@
  */
 public class InvocationEvent extends AWTEvent implements ActiveEvent {
 
+    static {
+        AWTAccessor.setInvocationEventAccessor(new AWTAccessor.InvocationEventAccessor() {
+            @Override
+            public void dispose(InvocationEvent invocationEvent) {
+                invocationEvent.finishedDispatching(false);
+            }
+        });
+    }
+
     /**
      * Marks the first integer id for the range of invocation event ids.
      */
@@ -78,11 +89,21 @@
 
     /**
      * The (potentially null) Object whose notifyAll() method will be called
-     * immediately after the Runnable.run() method has returned or thrown an exception.
+     * immediately after the Runnable.run() method has returned or thrown an exception
+     * or after the event was disposed.
      *
      * @see #isDispatched
      */
-    protected Object notifier;
+    protected volatile Object notifier;
+
+    /**
+     * The (potentially null) Runnable whose run() method will be called
+     * immediately after the event was dispatched or disposed.
+     *
+     * @see #isDispatched
+     * @since 1.8
+     */
+    private final Runnable listener;
 
     /**
      * Indicates whether the <code>run()</code> method of the <code>runnable</code>
@@ -147,7 +168,7 @@
      * @see #InvocationEvent(Object, Runnable, Object, boolean)
      */
     public InvocationEvent(Object source, Runnable runnable) {
-        this(source, runnable, null, false);
+        this(source, INVOCATION_DEFAULT, runnable, null, null, false);
     }
 
     /**
@@ -171,7 +192,8 @@
      * @param notifier          The {@code Object} whose <code>notifyAll</code>
      *                          method will be called after
      *                          <code>Runnable.run</code> has returned or
-     *                          thrown an exception
+     *                          thrown an exception or after the event was
+     *                          disposed
      * @param catchThrowables   Specifies whether <code>dispatch</code>
      *                          should catch Throwable when executing
      *                          the <code>Runnable</code>'s <code>run</code>
@@ -185,7 +207,39 @@
      */
     public InvocationEvent(Object source, Runnable runnable, Object notifier,
                            boolean catchThrowables) {
-        this(source, INVOCATION_DEFAULT, runnable, notifier, catchThrowables);
+        this(source, INVOCATION_DEFAULT, runnable, notifier, null, catchThrowables);
+    }
+
+    /**
+     * Constructs an <code>InvocationEvent</code> with the specified
+     * source which will execute the runnable's <code>run</code>
+     * method when dispatched.  If listener is non-<code>null</code>,
+     * <code>listener.run()</code> will be called immediately after
+     * <code>run</code> has returned, thrown an exception or the event
+     * was disposed.
+     * <p>This method throws an <code>IllegalArgumentException</code>
+     * if <code>source</code> is <code>null</code>.
+     *
+     * @param source            The <code>Object</code> that originated
+     *                          the event
+     * @param runnable          The <code>Runnable</code> whose
+     *                          <code>run</code> method will be
+     *                          executed
+     * @param listener          The <code>Runnable</code>Runnable whose
+     *                          <code>run()</code> method will be called
+     *                          after the {@code InvocationEvent}
+     *                          was dispatched or disposed
+     * @param catchThrowables   Specifies whether <code>dispatch</code>
+     *                          should catch Throwable when executing
+     *                          the <code>Runnable</code>'s <code>run</code>
+     *                          method, or should instead propagate those
+     *                          Throwables to the EventDispatchThread's
+     *                          dispatch loop
+     * @throws IllegalArgumentException if <code>source</code> is null
+     */
+    public InvocationEvent(Object source, Runnable runnable, Runnable listener,
+                           boolean catchThrowables)  {
+        this(source, INVOCATION_DEFAULT, runnable, null, listener, catchThrowables);
     }
 
     /**
@@ -208,7 +262,8 @@
      * @param notifier          The <code>Object</code> whose <code>notifyAll</code>
      *                          method will be called after
      *                          <code>Runnable.run</code> has returned or
-     *                          thrown an exception
+     *                          thrown an exception or after the event was
+     *                          disposed
      * @param catchThrowables   Specifies whether <code>dispatch</code>
      *                          should catch Throwable when executing the
      *                          <code>Runnable</code>'s <code>run</code>
@@ -221,13 +276,18 @@
      */
     protected InvocationEvent(Object source, int id, Runnable runnable,
                               Object notifier, boolean catchThrowables) {
+        this(source, id, runnable, notifier, null, catchThrowables);
+    }
+
+    private InvocationEvent(Object source, int id, Runnable runnable,
+                            Object notifier, Runnable listener, boolean catchThrowables) {
         super(source, id);
         this.runnable = runnable;
         this.notifier = notifier;
+        this.listener = listener;
         this.catchExceptions = catchThrowables;
         this.when = System.currentTimeMillis();
     }
-
     /**
      * Executes the Runnable's <code>run()</code> method and notifies the
      * notifier (if any) when <code>run()</code> has returned or thrown an exception.
@@ -251,13 +311,7 @@
                 runnable.run();
             }
         } finally {
-            dispatched = true;
-
-            if (notifier != null) {
-                synchronized (notifier) {
-                    notifier.notifyAll();
-                }
-            }
+            finishedDispatching(true);
         }
     }
 
@@ -331,6 +385,25 @@
     }
 
     /**
+     * Called when the event was dispatched or disposed
+     * @param dispatched true if the event was dispatched
+     *                   false if the event was disposed
+     */
+    private void finishedDispatching(boolean dispatched) {
+        this.dispatched = dispatched;
+
+        if (notifier != null) {
+            synchronized (notifier) {
+                notifier.notifyAll();
+            }
+        }
+
+        if (listener != null) {
+            listener.run();
+        }
+    }
+
+    /**
      * Returns a parameter string identifying this event.
      * This method is useful for event-logging and for debugging.
      *
--- a/jdk/src/share/classes/sun/awt/AWTAccessor.java	Thu Oct 10 02:35:27 2013 +0400
+++ b/jdk/src/share/classes/sun/awt/AWTAccessor.java	Thu Oct 10 11:40:06 2013 +0400
@@ -31,6 +31,7 @@
 import java.awt.KeyboardFocusManager;
 import java.awt.DefaultKeyboardFocusManager;
 import java.awt.event.InputEvent;
+import java.awt.event.InvocationEvent;
 import java.awt.event.KeyEvent;
 import java.awt.geom.Point2D;
 import java.awt.peer.ComponentPeer;
@@ -721,6 +722,13 @@
     }
 
     /*
+     * An accessor object for the InvocationEvent class
+     */
+    public interface InvocationEventAccessor {
+        void dispose(InvocationEvent event);
+    }
+
+    /*
      * Accessor instances are initialized in the static initializers of
      * corresponding AWT classes by using setters defined below.
      */
@@ -748,6 +756,7 @@
     private static DefaultKeyboardFocusManagerAccessor defaultKeyboardFocusManagerAccessor;
     private static SequencedEventAccessor sequencedEventAccessor;
     private static ToolkitAccessor toolkitAccessor;
+    private static InvocationEventAccessor invocationEventAccessor;
 
     /*
      * Set an accessor object for the java.awt.Component class.
@@ -1159,4 +1168,18 @@
 
         return toolkitAccessor;
     }
+
+    /*
+     * Get the accessor object for the java.awt.event.InvocationEvent class.
+     */
+    public static void setInvocationEventAccessor(InvocationEventAccessor invocationEventAccessor) {
+        AWTAccessor.invocationEventAccessor = invocationEventAccessor;
+    }
+
+    /*
+     * Set the accessor object for the java.awt.event.InvocationEvent class.
+     */
+    public static InvocationEventAccessor getInvocationEventAccessor() {
+        return invocationEventAccessor;
+    }
 }