OpenJDK / amber / amber
changeset 4264:40c232605c68
6852111: Unhandled 'spurious wakeup' in java.awt.EventQueue.invokeAndWait()
Summary: Introduced InvocationEvent.isDispatched method
Reviewed-by: art, anthony
author | dcherepanov |
---|---|
date | Wed, 11 Nov 2009 17:46:58 +0300 |
parents | db2d1f69a60e |
children | 8950a3d33a86 |
files | jdk/src/share/classes/java/awt/EventQueue.java jdk/src/share/classes/java/awt/event/InvocationEvent.java jdk/src/share/classes/sun/awt/im/InputMethodManager.java jdk/test/java/awt/event/InvocationEvent/InvocationEventTest.java |
diffstat | 4 files changed, 149 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- 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 "); + } + } +}