From 1fbf7057398a125410129307e87aba43f8afd92e Mon Sep 17 00:00:00 2001 From: Dmitry Cherepanov Date: Wed, 11 Nov 2009 17:46:58 +0300 Subject: [PATCH] 6852111: Unhandled 'spurious wakeup' in java.awt.EventQueue.invokeAndWait() Introduced InvocationEvent.isDispatched method Reviewed-by: art, anthony --- .../share/classes/java/awt/EventQueue.java | 4 +- .../java/awt/event/InvocationEvent.java | 97 ++++++++++++++----- .../sun/awt/im/InputMethodManager.java | 4 +- .../InvocationEvent/InvocationEventTest.java | 68 +++++++++++++ 4 files changed, 149 insertions(+), 24 deletions(-) create mode 100644 jdk/test/java/awt/event/InvocationEvent/InvocationEventTest.java diff --git a/jdk/src/share/classes/java/awt/EventQueue.java b/jdk/src/share/classes/java/awt/EventQueue.java index 3e9febf79b7..b5635f4a25c 100644 --- a/jdk/src/share/classes/java/awt/EventQueue.java +++ b/jdk/src/share/classes/java/awt/EventQueue.java @@ -1027,7 +1027,9 @@ public class EventQueue { synchronized (lock) { Toolkit.getEventQueue().postEvent(event); - lock.wait(); + while (!event.isDispatched()) { + lock.wait(); + } } Throwable eventThrowable = event.getThrowable(); diff --git a/jdk/src/share/classes/java/awt/event/InvocationEvent.java b/jdk/src/share/classes/java/awt/event/InvocationEvent.java index 0959a86cb35..e21adf352bb 100644 --- a/jdk/src/share/classes/java/awt/event/InvocationEvent.java +++ b/jdk/src/share/classes/java/awt/event/InvocationEvent.java @@ -78,10 +78,21 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { /** * 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 run() method of the runnable + * 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 @@ -144,7 +155,7 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { * source which will execute the runnable's run * method when dispatched. If notifier is non-null, * notifyAll() will be called on it - * immediately after run returns. + * immediately after run has returned or thrown an exception. *

An invocation of the form InvocationEvent(source, * runnable, notifier, catchThrowables) * behaves in exactly the same way as the invocation of @@ -159,7 +170,8 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { * executed * @param notifier The {@code Object} whose notifyAll * method will be called after - * Runnable.run has returned + * Runnable.run has returned or + * thrown an exception * @param catchThrowables Specifies whether dispatch * should catch Throwable when executing * the Runnable's run @@ -180,8 +192,8 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { * Constructs an InvocationEvent with the specified * source and ID which will execute the runnable's run * method when dispatched. If notifier is non-null, - * notifyAll will be called on it - * immediately after run returns. + * notifyAll will be called on it immediately after + * run has returned or thrown an exception. *

This method throws an * IllegalArgumentException if source * is null. @@ -195,7 +207,8 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { * run method will be executed * @param notifier The Object whose notifyAll * method will be called after - * Runnable.run has returned + * Runnable.run has returned or + * thrown an exception * @param catchThrowables Specifies whether dispatch * should catch Throwable when executing the * Runnable's run @@ -217,27 +230,33 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { /** * Executes the Runnable's run() method and notifies the - * notifier (if any) when run() returns. + * notifier (if any) when run() 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; - } - throwable = t; - } - } - else { - runnable.run(); - } + } finally { + dispatched = true; - if (notifier != null) { - synchronized (notifier) { - notifier.notifyAll(); + if (notifier != null) { + synchronized (notifier) { + notifier.notifyAll(); + } } } } @@ -277,6 +296,40 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { return when; } + /** + * 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: + *

+     *     while (!event.isDispatched()) {
+     *         notifier.wait();
+     *     }
+     * 
+ * 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. + *

+ * 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. diff --git a/jdk/src/share/classes/sun/awt/im/InputMethodManager.java b/jdk/src/share/classes/sun/awt/im/InputMethodManager.java index caba8d6a152..9ee45e28e53 100644 --- a/jdk/src/share/classes/sun/awt/im/InputMethodManager.java +++ b/jdk/src/share/classes/sun/awt/im/InputMethodManager.java @@ -358,7 +358,9 @@ class ExecutableInputMethodManager extends InputMethodManager AppContext requesterAppContext = SunToolkit.targetToAppContext(requester); synchronized (lock) { SunToolkit.postEvent(requesterAppContext, event); - lock.wait(); + while (!event.isDispatched()) { + lock.wait(); + } } Throwable eventThrowable = event.getThrowable(); diff --git a/jdk/test/java/awt/event/InvocationEvent/InvocationEventTest.java b/jdk/test/java/awt/event/InvocationEvent/InvocationEventTest.java new file mode 100644 index 00000000000..5fb83d10acf --- /dev/null +++ b/jdk/test/java/awt/event/InvocationEvent/InvocationEventTest.java @@ -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 "); + } + } +}