7186109: Simplify lock machinery for PostEventQueue & EventQueue
Reviewed-by: art, anthony, dholmes
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Wed Sep 12 21:16:39 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Thu Sep 13 19:53:13 2012 +0400
@@ -536,7 +536,7 @@
SunToolkit.postEvent(appContext, invocationEvent);
// 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
- sun.awt.SunToolkitSubclass.flushPendingEvents(appContext);
+ SunToolkit.flushPendingEvents(appContext);
} else {
// This should be the equivalent to EventQueue.invokeAndWait
((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
@@ -561,7 +561,7 @@
SunToolkit.postEvent(appContext, invocationEvent);
// 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
- sun.awt.SunToolkitSubclass.flushPendingEvents(appContext);
+ SunToolkit.flushPendingEvents(appContext);
} else {
// This should be the equivalent to EventQueue.invokeAndWait
((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
--- a/jdk/src/share/classes/java/awt/EventQueue.java Wed Sep 12 21:16:39 2012 +0400
+++ b/jdk/src/share/classes/java/awt/EventQueue.java Thu Sep 13 19:53:13 2012 +0400
@@ -1047,6 +1047,10 @@
final boolean detachDispatchThread(EventDispatchThread edt, boolean forceDetach) {
/*
+ * Minimize discard possibility for non-posted events
+ */
+ SunToolkit.flushPendingEvents();
+ /*
* This synchronized block is to secure that the event dispatch
* thread won't die in the middle of posting a new event to the
* associated event queue. It is important because we notify
@@ -1060,11 +1064,8 @@
/*
* Don't detach the thread if any events are pending. Not
* sure if it's a possible scenario, though.
- *
- * Fix for 4648733. Check both the associated java event
- * queue and the PostEventQueue.
*/
- if (!forceDetach && (peekEvent() != null) || !SunToolkit.isPostEventQueueEmpty()) {
+ if (!forceDetach && (peekEvent() != null)) {
return false;
}
dispatchThread = null;
--- a/jdk/src/share/classes/sun/awt/SunToolkit.java Wed Sep 12 21:16:39 2012 +0400
+++ b/jdk/src/share/classes/sun/awt/SunToolkit.java Thu Sep 13 19:53:13 2012 +0400
@@ -506,40 +506,25 @@
postEvent(targetToAppContext(e.getSource()), pe);
}
- protected static final Lock flushLock = new ReentrantLock();
- private static boolean isFlushingPendingEvents = false;
-
/*
* Flush any pending events which haven't been posted to the AWT
* EventQueue yet.
*/
public static void flushPendingEvents() {
- flushLock.lock();
- try {
- // Don't call flushPendingEvents() recursively
- if (!isFlushingPendingEvents) {
- isFlushingPendingEvents = true;
- AppContext appContext = AppContext.getAppContext();
- PostEventQueue postEventQueue =
- (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY);
- if (postEventQueue != null) {
- postEventQueue.flush();
- }
- }
- } finally {
- isFlushingPendingEvents = false;
- flushLock.unlock();
- }
+ AppContext appContext = AppContext.getAppContext();
+ flushPendingEvents(appContext);
}
- public static boolean isPostEventQueueEmpty() {
- AppContext appContext = AppContext.getAppContext();
+ /*
+ * Flush the PostEventQueue for the right AppContext.
+ * The default flushPendingEvents only flushes the thread-local context,
+ * which is not always correct, c.f. 3746956
+ */
+ public static void flushPendingEvents(AppContext appContext) {
PostEventQueue postEventQueue =
- (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY);
+ (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY);
if (postEventQueue != null) {
- return postEventQueue.noEvents();
- } else {
- return true;
+ postEventQueue.flush();
}
}
@@ -2045,17 +2030,12 @@
private EventQueueItem queueTail = null;
private final EventQueue eventQueue;
- // For the case when queue is cleared but events are not posted
- private volatile boolean isFlushing = false;
+ private Thread flushThread = null;
PostEventQueue(EventQueue eq) {
eventQueue = eq;
}
- public synchronized boolean noEvents() {
- return queueHead == null && !isFlushing;
- }
-
/*
* Continually post pending AWTEvents to the Java EventQueue. The method
* is synchronized to ensure the flush is completed before a new event
@@ -2066,20 +2046,48 @@
* potentially lead to deadlock
*/
public void flush() {
- EventQueueItem tempQueue;
- synchronized (this) {
- tempQueue = queueHead;
- queueHead = queueTail = null;
- isFlushing = true;
- }
+
+ Thread newThread = Thread.currentThread();
+
try {
- while (tempQueue != null) {
- eventQueue.postEvent(tempQueue.event);
- tempQueue = tempQueue.next;
+ EventQueueItem tempQueue;
+ synchronized (this) {
+ // Avoid method recursion
+ if (newThread == flushThread) {
+ return;
+ }
+ // Wait for other threads' flushing
+ while (flushThread != null) {
+ wait();
+ }
+ // Skip everything if queue is empty
+ if (queueHead == null) {
+ return;
+ }
+ // Remember flushing thread
+ flushThread = newThread;
+
+ tempQueue = queueHead;
+ queueHead = queueTail = null;
+ }
+ try {
+ while (tempQueue != null) {
+ eventQueue.postEvent(tempQueue.event);
+ tempQueue = tempQueue.next;
+ }
+ }
+ finally {
+ // Only the flushing thread can get here
+ synchronized (this) {
+ // Forget flushing thread, inform other pending threads
+ flushThread = null;
+ notifyAll();
+ }
}
}
- finally {
- isFlushing = false;
+ catch (InterruptedException e) {
+ // Couldn't allow exception go up, so at least recover the flag
+ newThread.interrupt();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/EventQueue/PostEventOrderingTest/PostEventOrderingTest.java Thu Sep 13 19:53:13 2012 +0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test PostEventOrderingTest.java
+ * @bug 4171596 6699589
+ * @summary Checks that the posting of events between the PostEventQueue
+ * @summary and the EventQueue maintains proper ordering.
+ * @run main PostEventOrderingTest
+ * @author fredx
+ */
+
+import java.awt.*;
+import java.awt.event.*;
+import sun.awt.AppContext;
+import sun.awt.SunToolkit;
+
+public class PostEventOrderingTest {
+ static boolean testPassed = true;
+
+ public static void main(String[] args) throws Throwable {
+ EventQueue q = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ for (int i = 0; i < 100; i++) {
+ for (int j = 0; j < 100; j++) {
+ q.postEvent(new PostActionEvent());
+ for (int k = 0; k < 10; k++) {
+ SunToolkit.postEvent(AppContext.getAppContext(), new PostActionEvent());
+ }
+ }
+ for (int k = 0; k < 100; k++) {
+ SunToolkit.postEvent(AppContext.getAppContext(), new PostActionEvent());
+ }
+ }
+
+ for (;;) {
+ Thread.currentThread().sleep(100);
+ if (q.peekEvent() == null) {
+ Thread.currentThread().sleep(100);
+ if (q.peekEvent() == null)
+ break;
+ }
+ }
+
+ if (!testPassed) {
+ throw new Exception("PostEventOrderingTest FAILED -- events dispatched out of order.");
+ } else {
+ System.out.println("PostEventOrderingTest passed!");
+ }
+ }
+}
+
+class PostActionEvent extends ActionEvent implements ActiveEvent {
+ static int counter = 0;
+ static int mostRecent = -1;
+
+ int myval;
+
+ public PostActionEvent() {
+ super("", ACTION_PERFORMED, "" + counter);
+ myval = counter++;
+ }
+
+ public void dispatch() {
+ //System.out.println("myval = "+myval+", mostRecent = "+mostRecent+", diff = "+(myval-mostRecent)+".");
+ if ((myval - mostRecent) != 1)
+ PostEventOrderingTest.testPassed = false;
+ mostRecent = myval;
+ }
+}