6852111: Unhandled 'spurious wakeup' in java.awt.EventQueue.invokeAndWait()

Introduced InvocationEvent.isDispatched method

Reviewed-by: art, anthony
This commit is contained in:
Dmitry Cherepanov 2009-11-11 17:46:58 +03:00
parent 85e10718ce
commit 1fbf705739
4 changed files with 149 additions and 24 deletions

View File

@ -1027,7 +1027,9 @@ public class EventQueue {
synchronized (lock) {
Toolkit.getEventQueue().postEvent(event);
lock.wait();
while (!event.isDispatched()) {
lock.wait();
}
}
Throwable eventThrowable = event.getThrowable();

View File

@ -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 <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
@ -144,7 +155,7 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent {
* 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 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent {
* 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 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent {
* 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 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent {
* <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 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent {
/**
* 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;
}
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:
* <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.

View File

@ -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();

View File

@ -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 ");
}
}
}