6852111: Unhandled 'spurious wakeup' in java.awt.EventQueue.invokeAndWait()
Summary: Introduced InvocationEvent.isDispatched method
Reviewed-by: art, anthony
--- a/jdk/src/share/classes/java/awt/EventQueue.java Fri Oct 23 14:52:55 2009 +0400
+++ b/jdk/src/share/classes/java/awt/EventQueue.java Wed Nov 11 17:46:58 2009 +0300
@@ -1027,7 +1027,9 @@
synchronized (lock) {
Toolkit.getEventQueue().postEvent(event);
- lock.wait();
+ while (!event.isDispatched()) {
+ lock.wait();
+ }
}
Throwable eventThrowable = event.getThrowable();
--- a/jdk/src/share/classes/java/awt/event/InvocationEvent.java Fri Oct 23 14:52:55 2009 +0400
+++ b/jdk/src/share/classes/java/awt/event/InvocationEvent.java Wed Nov 11 17:46:58 2009 +0300
@@ -78,11 +78,22 @@
/**
* The (potentially null) Object whose notifyAll() method will be called
- * immediately after the Runnable.run() method returns.
+ * immediately after the Runnable.run() method has returned or thrown an exception.
+ *
+ * @see #isDispatched
*/
protected Object notifier;
/**
+ * Indicates whether the <code>run()</code> method of the <code>runnable</code>
+ * was executed or not.
+ *
+ * @see #isDispatched
+ * @since 1.7
+ */
+ private volatile boolean dispatched = false;
+
+ /**
* Set to true if dispatch() catches Throwable and stores it in the
* exception instance variable. If false, Throwables are propagated up
* to the EventDispatchThread's dispatch loop.
@@ -144,7 +155,7 @@
* source which will execute the runnable's <code>run</code>
* method when dispatched. If notifier is non-<code>null</code>,
* <code>notifyAll()</code> will be called on it
- * immediately after <code>run</code> returns.
+ * immediately after <code>run</code> has returned or thrown an exception.
* <p>An invocation of the form <tt>InvocationEvent(source,
* runnable, notifier, catchThrowables)</tt>
* behaves in exactly the same way as the invocation of
@@ -159,7 +170,8 @@
* executed
* @param notifier The {@code Object} whose <code>notifyAll</code>
* method will be called after
- * <code>Runnable.run</code> has returned
+ * <code>Runnable.run</code> has returned or
+ * thrown an exception
* @param catchThrowables Specifies whether <code>dispatch</code>
* should catch Throwable when executing
* the <code>Runnable</code>'s <code>run</code>
@@ -180,8 +192,8 @@
* Constructs an <code>InvocationEvent</code> with the specified
* source and ID which will execute the runnable's <code>run</code>
* method when dispatched. If notifier is non-<code>null</code>,
- * <code>notifyAll</code> will be called on it
- * immediately after <code>run</code> returns.
+ * <code>notifyAll</code> will be called on it immediately after
+ * <code>run</code> has returned or thrown an exception.
* <p>This method throws an
* <code>IllegalArgumentException</code> if <code>source</code>
* is <code>null</code>.
@@ -195,7 +207,8 @@
* <code>run</code> method will be executed
* @param notifier The <code>Object</code> whose <code>notifyAll</code>
* method will be called after
- * <code>Runnable.run</code> has returned
+ * <code>Runnable.run</code> has returned or
+ * thrown an exception
* @param catchThrowables Specifies whether <code>dispatch</code>
* should catch Throwable when executing the
* <code>Runnable</code>'s <code>run</code>
@@ -217,27 +230,33 @@
/**
* Executes the Runnable's <code>run()</code> method and notifies the
- * notifier (if any) when <code>run()</code> returns.
+ * notifier (if any) when <code>run()</code> has returned or thrown an exception.
+ *
+ * @see #isDispatched
*/
public void dispatch() {
- if (catchExceptions) {
- try {
+ try {
+ if (catchExceptions) {
+ try {
+ runnable.run();
+ }
+ catch (Throwable t) {
+ if (t instanceof Exception) {
+ exception = (Exception) t;
+ }
+ throwable = t;
+ }
+ }
+ else {
runnable.run();
}
- catch (Throwable t) {
- if (t instanceof Exception) {
- exception = (Exception) t;
+ } finally {
+ dispatched = true;
+
+ if (notifier != null) {
+ synchronized (notifier) {
+ notifier.notifyAll();
}
- throwable = t;
- }
- }
- else {
- runnable.run();
- }
-
- if (notifier != null) {
- synchronized (notifier) {
- notifier.notifyAll();
}
}
}
@@ -278,6 +297,40 @@
}
/**
+ * Returns {@code true} if the event is dispatched or any exception is
+ * thrown while dispatching, {@code false} otherwise. The method should
+ * be called by a waiting thread that calls the {@code notifier.wait()} method.
+ * Since spurious wakeups are possible (as explained in {@link Object#wait()}),
+ * this method should be used in a waiting loop to ensure that the event
+ * got dispatched:
+ * <pre>
+ * while (!event.isDispatched()) {
+ * notifier.wait();
+ * }
+ * </pre>
+ * If the waiting thread wakes up without dispatching the event,
+ * the {@code isDispatched()} method returns {@code false}, and
+ * the {@code while} loop executes once more, thus, causing
+ * the awakened thread to revert to the waiting mode.
+ * <p>
+ * If the {@code notifier.notifyAll()} happens before the waiting thread
+ * enters the {@code notifier.wait()} method, the {@code while} loop ensures
+ * that the waiting thread will not enter the {@code notifier.wait()} method.
+ * Otherwise, there is no guarantee that the waiting thread will ever be woken
+ * from the wait.
+ *
+ * @return {@code true} if the event has been dispatched, or any exception
+ * has been thrown while dispatching, {@code false} otherwise
+ * @see #dispatch
+ * @see #notifier
+ * @see #catchExceptions
+ * @since 1.7
+ */
+ public boolean isDispatched() {
+ return dispatched;
+ }
+
+ /**
* Returns a parameter string identifying this event.
* This method is useful for event-logging and for debugging.
*
--- a/jdk/src/share/classes/sun/awt/im/InputMethodManager.java Fri Oct 23 14:52:55 2009 +0400
+++ b/jdk/src/share/classes/sun/awt/im/InputMethodManager.java Wed Nov 11 17:46:58 2009 +0300
@@ -358,7 +358,9 @@
AppContext requesterAppContext = SunToolkit.targetToAppContext(requester);
synchronized (lock) {
SunToolkit.postEvent(requesterAppContext, event);
- lock.wait();
+ while (!event.isDispatched()) {
+ lock.wait();
+ }
}
Throwable eventThrowable = event.getThrowable();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/event/InvocationEvent/InvocationEventTest.java Wed Nov 11 17:46:58 2009 +0300
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ @test
+ @bug 6852111
+ @summary Unhandled 'spurious wakeup' in java.awt.EventQueue.invokeAndWait()
+ @author dmitry.cherepanov@sun.com: area=awt.event
+ @run main InvocationEventTest
+*/
+
+/**
+ * InvocationEventTest.java
+ *
+ * summary: Tests new isDispatched method of the InvocationEvent class
+ */
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class InvocationEventTest
+{
+ public static void main(String []s) throws Exception
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ Runnable runnable = new Runnable() {
+ public void run() {
+ }
+ };
+ Object lock = new Object();
+ InvocationEvent event = new InvocationEvent(tk, runnable, lock, true);
+
+ if (event.isDispatched()) {
+ throw new RuntimeException(" Initially, the event shouldn't be dispatched ");
+ }
+
+ synchronized(lock) {
+ tk.getSystemEventQueue().postEvent(event);
+ while(!event.isDispatched()) {
+ lock.wait();
+ }
+ }
+
+ if(!event.isDispatched()) {
+ throw new RuntimeException(" Finally, the event should be dispatched ");
+ }
+ }
+}