changeset 7075:26522cc1beae

8025588: [macosx] Frozen AppKit thread in 7u40 Reviewed-by: anthony, art, serb
author pchelko
date Thu, 28 Nov 2013 10:33:52 +0400
parents a41e6d94840b
children 59b9673236c2
files src/macosx/classes/sun/lwawt/macosx/CInputMethod.java src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java src/share/classes/java/awt/EventQueue.java src/share/classes/java/awt/event/InvocationEvent.java src/share/classes/sun/awt/AWTAccessor.java
diffstat 7 files changed, 143 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java	Thu Nov 28 10:33:52 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/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Nov 28 10:33:52 2013 +0400
@@ -918,7 +918,7 @@
                         //Posting an empty to flush the EventQueue without blocking the main thread
                     }
                 }, target);
-            } catch (InterruptedException | InvocationTargetException e) {
+            } catch (InvocationTargetException e) {
                 e.printStackTrace();
             }
         }
--- a/src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java	Thu Nov 28 10:33:52 2013 +0400
@@ -97,6 +97,6 @@
                     setVisible(true);
                 }
             }, this);
-        } catch (InterruptedException | InvocationTargetException ex) {}
+        } catch (InvocationTargetException ex) {}
     }
 }
--- a/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java	Thu Nov 28 10:33:52 2013 +0400
@@ -518,22 +518,21 @@
     // 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 {
-                            if (mediator != 0) {
-                                stopAWTRunLoop(mediator);
+        InvocationEvent invocationEvent = AWTAccessor.getInvocationEventAccessor()
+                .createEvent(component != null ? component : Toolkit.getDefaultToolkit(),
+                        runnable,
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mediator != 0) {
+                                    stopAWTRunLoop(mediator);
+                                }
                             }
-                        }
-                    }
-                };
+                        },
+                        true);
 
         if (component != null) {
             AppContext appContext = SunToolkit.targetToAppContext(component);
--- a/src/share/classes/java/awt/EventQueue.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/share/classes/java/awt/EventQueue.java	Thu Nov 28 10:33:52 2013 +0400
@@ -1139,6 +1139,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/src/share/classes/java/awt/event/InvocationEvent.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/share/classes/java/awt/event/InvocationEvent.java	Thu Nov 28 10:33:52 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,20 @@
  */
 public class InvocationEvent extends AWTEvent implements ActiveEvent {
 
+    static {
+        AWTAccessor.setInvocationEventAccessor(new AWTAccessor.InvocationEventAccessor() {
+            @Override
+            public void dispose(InvocationEvent invocationEvent) {
+                invocationEvent.finishedDispatching(false);
+            }
+            @Override
+            public InvocationEvent createEvent(Object source, Runnable runnable, Runnable listener,
+                                               boolean catchThrowables) {
+                return new InvocationEvent(source, runnable, listener, catchThrowables);
+            }
+        });
+    }
+
     /**
      * Marks the first integer id for the range of invocation event ids.
      */
@@ -82,7 +98,15 @@
      *
      * @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
+     */
+    private final Runnable listener;
 
     /**
      * Indicates whether the <code>run()</code> method of the <code>runnable</code>
@@ -147,7 +171,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);
     }
 
     /**
@@ -185,7 +209,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
+     */
+    private InvocationEvent(Object source, Runnable runnable, Runnable listener,
+                           boolean catchThrowables)  {
+        this(source, INVOCATION_DEFAULT, runnable, null, listener, catchThrowables);
     }
 
     /**
@@ -221,13 +277,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 +312,7 @@
                 runnable.run();
             }
         } finally {
-            dispatched = true;
-
-            if (notifier != null) {
-                synchronized (notifier) {
-                    notifier.notifyAll();
-                }
-            }
+            finishedDispatching(true);
         }
     }
 
@@ -331,6 +386,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/src/share/classes/sun/awt/AWTAccessor.java	Wed Nov 27 11:20:48 2013 -0800
+++ b/src/share/classes/sun/awt/AWTAccessor.java	Thu Nov 28 10:33:52 2013 +0400
@@ -29,6 +29,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;
 
@@ -690,6 +691,25 @@
     }
 
     /*
+     * An accessor object for the InvocationEvent class
+     */
+    public interface InvocationEventAccessor {
+        /**
+         * Disposes the InvocationEvent
+         */
+        void dispose(InvocationEvent event);
+
+        /**
+         * Creates an InvocationEvent with a completion listener -
+         * a Runnable whose run() method will be called immediately after
+         * the event is dispatched or disposed
+         */
+        InvocationEvent createEvent(Object source, Runnable runnable, Runnable listener,
+                                    boolean catchThrowables);
+    }
+
+
+    /*
      * Accessor instances are initialized in the static initializers of
      * corresponding AWT classes by using setters defined below.
      */
@@ -716,6 +736,7 @@
     private static TrayIconAccessor trayIconAccessor;
     private static DefaultKeyboardFocusManagerAccessor defaultKeyboardFocusManagerAccessor;
     private static SequencedEventAccessor sequencedEventAccessor;
+    private static InvocationEventAccessor invocationEventAccessor;
 
     /*
      * Set an accessor object for the java.awt.Component class.
@@ -1110,4 +1131,18 @@
         // (so not a single instance of the event has been created).
         return sequencedEventAccessor;
     }
+
+    /*
+     * 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;
+    }
 }