# HG changeset patch # User bagiras # Date 1347551593 -14400 # Node ID 3ab536cab1d144d1a22bb00b3cccd5931287ed84 # Parent 62bde3c50b975dbbdaffab37d32456189e49dd77 7186109: Simplify lock machinery for PostEventQueue & EventQueue Reviewed-by: art, anthony, dholmes diff -r 62bde3c50b97 -r 3ab536cab1d1 jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java --- 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); diff -r 62bde3c50b97 -r 3ab536cab1d1 jdk/src/share/classes/java/awt/EventQueue.java --- 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; diff -r 62bde3c50b97 -r 3ab536cab1d1 jdk/src/share/classes/sun/awt/SunToolkit.java --- 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(); } } diff -r 62bde3c50b97 -r 3ab536cab1d1 jdk/test/java/awt/EventQueue/PostEventOrderingTest/PostEventOrderingTest.java --- /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; + } +}