changeset 2520:bccf2a4ee318

6424157: java.awt.EventQueue push/pop might cause threading issues Reviewed-by: ant, dcherepanov
author art
date Tue, 06 Jul 2010 17:59:56 +0400
parents f5145c7119c2
children 21b17c64df74
files src/share/classes/java/awt/EventDispatchThread.java src/share/classes/java/awt/EventQueue.java src/share/classes/sun/awt/SunToolkit.java test/java/awt/EventDispatchThread/HandleExceptionOnEDT/HandleExceptionOnEDT.java test/java/awt/EventDispatchThread/LoopRobustness/LoopRobustness.java test/java/awt/EventDispatchThread/PreserveDispathThread/PreserveDispatchThread.java test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java
diffstat 7 files changed, 471 insertions(+), 227 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/awt/EventDispatchThread.java	Thu Jun 24 11:50:18 2010 +0400
+++ b/src/share/classes/java/awt/EventDispatchThread.java	Tue Jul 06 17:59:56 2010 +0400
@@ -61,85 +61,43 @@
  * @since 1.1
  */
 class EventDispatchThread extends Thread {
+
     private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventDispatchThread");
 
     private EventQueue theQueue;
     private boolean doDispatch = true;
+    private boolean threadDeathCaught = false;
+
     private static final int ANY_EVENT = -1;
 
     private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
-    // used in handleException
-    private int modalFiltersCount = 0;
 
     EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
         super(group, name);
-        theQueue = queue;
+        setEventQueue(queue);
     }
 
-    void stopDispatchingImpl(boolean wait) {
-        // Note: We stop dispatching via a flag rather than using
-        // Thread.interrupt() because we can't guarantee that the wait()
-        // we interrupt will be EventQueue.getNextEvent()'s.  -fredx 8-11-98
-
-        StopDispatchEvent stopEvent = new StopDispatchEvent();
-
-        // wait for the dispatcher to complete
-        if (Thread.currentThread() != this) {
-
-            // fix 4122683, 4128923
-            // Post an empty event to ensure getNextEvent is unblocked
-            //
-            // We have to use postEventPrivate instead of postEvent because
-            // EventQueue.pop calls EventDispatchThread.stopDispatching.
-            // Calling SunToolkit.flushPendingEvents in this case could
-            // lead to deadlock.
-            theQueue.postEventPrivate(stopEvent);
-
-            if (wait) {
-                try {
-                    join();
-                } catch(InterruptedException e) {
-                }
-            }
-        } else {
-            stopEvent.dispatch();
-        }
-
-        theQueue.detachDispatchThread(this, false);
-    }
-
+    /*
+     * Must be called on EDT only, that's why no synchronization
+     */
     public void stopDispatching() {
-        stopDispatchingImpl(true);
-    }
-
-    public void stopDispatchingLater() {
-        stopDispatchingImpl(false);
-    }
-
-    class StopDispatchEvent extends AWTEvent implements ActiveEvent {
-        /*
-         * serialVersionUID
-         */
-        static final long serialVersionUID = -3692158172100730735L;
-
-        public StopDispatchEvent() {
-            super(EventDispatchThread.this,0);
-        }
-
-        public void dispatch() {
-            doDispatch = false;
-        }
+        doDispatch = false;
     }
 
     public void run() {
-        try {
-            pumpEvents(new Conditional() {
-                public boolean evaluate() {
-                    return true;
+        while (true) {
+            try {
+                pumpEvents(new Conditional() {
+                    public boolean evaluate() {
+                        return true;
+                    }
+                });
+            } finally {
+                EventQueue eq = getEventQueue();
+                if (eq.detachDispatchThread(this) || threadDeathCaught) {
+                    break;
                 }
-            });
-        } finally {
-            theQueue.detachDispatchThread(this, true);
+            }
         }
     }
 
@@ -190,7 +148,6 @@
                         }
                     }
                     eventFilters.add(k, filter);
-                    modalFiltersCount++;
                 } else {
                     eventFilters.add(filter);
                 }
@@ -200,28 +157,25 @@
 
     void removeEventFilter(EventFilter filter) {
         synchronized (eventFilters) {
-            if (eventFilters.contains(filter)) {
-                if (filter instanceof ModalEventFilter) {
-                    modalFiltersCount--;
-                }
-                eventFilters.remove(filter);
-            }
+            eventFilters.remove(filter);
         }
     }
 
     boolean pumpOneEventForFilters(int id) {
+        AWTEvent event = null;
+        boolean eventOK = false;
         try {
-            AWTEvent event;
-            boolean eventOK;
-            EventQueueDelegate.Delegate delegate =
-                EventQueueDelegate.getDelegate();
+            EventQueue eq = null;
+            EventQueueDelegate.Delegate delegate = null;
             do {
+                // EventQueue may change during the dispatching
+                eq = getEventQueue();
+                delegate = EventQueueDelegate.getDelegate();
+
                 if (delegate != null && id == ANY_EVENT) {
-                    event = delegate.getNextEvent(theQueue);
+                    event = delegate.getNextEvent(eq);
                 } else {
-                    event = (id == ANY_EVENT)
-                        ? theQueue.getNextEvent()
-                        : theQueue.getNextEvent(id);
+                    event = (id == ANY_EVENT) ? eq.getNextEvent() : eq.getNextEvent(id);
                 }
 
                 eventOK = true;
@@ -252,13 +206,15 @@
             if (delegate != null) {
                 handle = delegate.beforeDispatch(event);
             }
-            theQueue.dispatchEvent(event);
+            eq.dispatchEvent(event);
             if (delegate != null) {
                 delegate.afterDispatch(event, handle);
             }
+
             return true;
         }
         catch (ThreadDeath death) {
+            threadDeathCaught = true;
             return false;
 
         }
@@ -267,12 +223,10 @@
                           // Threads in the AppContext
 
         }
-        // Can get and throw only unchecked exceptions
-        catch (RuntimeException e) {
-            processException(e);
-        } catch (Error e) {
+        catch (Throwable e) {
             processException(e);
         }
+
         return true;
     }
 
@@ -281,14 +235,14 @@
             eventLog.fine("Processing exception: " + e);
         }
         getUncaughtExceptionHandler().uncaughtException(this, e);
-        // don't rethrow the exception to avoid EDT recreation
     }
 
-    boolean isDispatching(EventQueue eq) {
-        return theQueue.equals(eq);
+    public synchronized EventQueue getEventQueue() {
+        return theQueue;
     }
-
-    EventQueue getEventQueue() { return theQueue; }
+    public synchronized void setEventQueue(EventQueue eq) {
+        theQueue = eq;
+    }
 
     private static class HierarchyEventFilter implements EventFilter {
         private Component modalComponent;
--- a/src/share/classes/java/awt/EventQueue.java	Thu Jun 24 11:50:18 2010 +0400
+++ b/src/share/classes/java/awt/EventQueue.java	Tue Jul 06 17:59:56 2010 +0400
@@ -138,6 +138,15 @@
     private final Lock pushPopLock;
     private final Condition pushPopCond;
 
+    /*
+     * Dummy runnable to wake up EDT from getNextEvent() after
+     push/pop is performed
+     */
+    private final static Runnable dummyRunnable = new Runnable() {
+        public void run() {
+        }
+    };
+
     private EventDispatchThread dispatchThread;
 
     private final ThreadGroup threadGroup =
@@ -219,22 +228,22 @@
      * @param theEvent an instance of <code>java.awt.AWTEvent</code>,
      *          or a subclass of it
      */
-    final void postEventPrivate(AWTEvent theEvent) {
+    private final void postEventPrivate(AWTEvent theEvent) {
         theEvent.isPosted = true;
         pushPopLock.lock();
         try {
-            if (dispatchThread == null && nextQueue == null) {
+            if (nextQueue != null) {
+                // Forward the event to the top of EventQueue stack
+                nextQueue.postEventPrivate(theEvent);
+                return;
+            }
+            if (dispatchThread == null) {
                 if (theEvent.getSource() == AWTAutoShutdown.getInstance()) {
                     return;
                 } else {
                     initDispatchThread();
                 }
             }
-            if (nextQueue != null) {
-                // Forward event to top of EventQueue stack.
-                nextQueue.postEventPrivate(theEvent);
-                return;
-            }
             postEvent(theEvent, getPriority(theEvent));
         } finally {
             pushPopLock.unlock();
@@ -242,29 +251,20 @@
     }
 
     private static int getPriority(AWTEvent theEvent) {
-        if (theEvent instanceof PeerEvent &&
-            (((PeerEvent)theEvent).getFlags() &
-                PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0)
-        {
-            return ULTIMATE_PRIORITY;
+        if (theEvent instanceof PeerEvent) {
+            PeerEvent peerEvent = (PeerEvent)theEvent;
+            if ((peerEvent.getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) {
+                return ULTIMATE_PRIORITY;
+            }
+            if ((peerEvent.getFlags() & PeerEvent.PRIORITY_EVENT) != 0) {
+                return HIGH_PRIORITY;
+            }
+            if ((peerEvent.getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) {
+                return LOW_PRIORITY;
+            }
         }
-
-        if (theEvent instanceof PeerEvent &&
-            (((PeerEvent)theEvent).getFlags() &
-                PeerEvent.PRIORITY_EVENT) != 0)
-        {
-            return HIGH_PRIORITY;
-        }
-
-        if (theEvent instanceof PeerEvent &&
-            (((PeerEvent)theEvent).getFlags() &
-                PeerEvent.LOW_PRIORITY_EVENT) != 0)
-        {
-            return LOW_PRIORITY;
-        }
-
         int id = theEvent.getID();
-        if (id == PaintEvent.PAINT || id == PaintEvent.UPDATE) {
+        if ((id >= PaintEvent.PAINT_FIRST) && (id <= PaintEvent.PAINT_LAST)) {
             return LOW_PRIORITY;
         }
         return NORM_PRIORITY;
@@ -501,16 +501,9 @@
             SunToolkit.flushPendingEvents();
             pushPopLock.lock();
             try {
-                for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
-                    if (queues[i].head != null) {
-                        EventQueueItem entry = queues[i].head;
-                        queues[i].head = entry.next;
-                        if (entry.next == null) {
-                            queues[i].tail = null;
-                        }
-                        uncacheEQItem(entry);
-                        return entry.event;
-                    }
+                AWTEvent event = getNextEventPrivate();
+                if (event != null) {
+                    return event;
                 }
                 AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread);
                 pushPopCond.await();
@@ -520,6 +513,24 @@
         } while(true);
     }
 
+    /*
+     * Must be called under the lock. Doesn't call flushPendingEvents()
+     */
+    AWTEvent getNextEventPrivate() throws InterruptedException {
+        for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
+            if (queues[i].head != null) {
+                EventQueueItem entry = queues[i].head;
+                queues[i].head = entry.next;
+                if (entry.next == null) {
+                    queues[i].tail = null;
+                }
+                uncacheEQItem(entry);
+                return entry.event;
+            }
+        }
+        return null;
+    }
+
     AWTEvent getNextEvent(int id) throws InterruptedException {
         do {
             /*
@@ -659,7 +670,9 @@
                 dispatchThread.stopDispatching();
             }
         } else {
-            System.err.println("unable to dispatch event: " + event);
+            if (eventLog.isLoggable(PlatformLogger.FINE)) {
+                eventLog.fine("Unable to dispatch event: " + event);
+            }
         }
     }
 
@@ -761,15 +774,23 @@
 
         pushPopLock.lock();
         try {
-            EventQueue toPush = this;
-            while (toPush.nextQueue != null) {
-                toPush = toPush.nextQueue;
+            EventQueue topQueue = this;
+            while (topQueue.nextQueue != null) {
+                topQueue = topQueue.nextQueue;
+            }
+
+            if ((topQueue.dispatchThread != null) &&
+                (topQueue.dispatchThread.getEventQueue() == this))
+            {
+                newEventQueue.dispatchThread = topQueue.dispatchThread;
+                topQueue.dispatchThread.setEventQueue(newEventQueue);
             }
 
             // Transfer all events forward to new EventQueue.
-            while (toPush.peekEvent() != null) {
+            while (topQueue.peekEvent() != null) {
                 try {
-                    newEventQueue.postEventPrivate(toPush.getNextEvent());
+                    // Use getNextEventPrivate() as it doesn't call flushPendingEvents()
+                    newEventQueue.postEventPrivate(topQueue.getNextEventPrivate());
                 } catch (InterruptedException ie) {
                     if (eventLog.isLoggable(PlatformLogger.FINE)) {
                         eventLog.fine("Interrupted push", ie);
@@ -777,28 +798,21 @@
                 }
             }
 
-            newEventQueue.previousQueue = toPush;
+            // Wake up EDT waiting in getNextEvent(), so it can
+            // pick up a new EventQueue. Post the waking event before
+            // topQueue.nextQueue is assigned, otherwise the event would
+            // go newEventQueue
+            topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
 
-            /*
-             * Stop the event dispatch thread associated with the currently
-             * active event queue, so that after the new queue is pushed
-             * on the top this event dispatch thread won't prevent AWT from
-             * being automatically shut down.
-             * Use stopDispatchingLater() to avoid deadlock: stopDispatching()
-             * waits for the dispatch thread to exit, which in turn waits
-             * for the lock in EQ.detachDispatchThread(), which is hold by
-             * this method.
-             */
-            if (toPush.dispatchThread != null) {
-                toPush.dispatchThread.stopDispatchingLater();
+            newEventQueue.previousQueue = topQueue;
+            topQueue.nextQueue = newEventQueue;
+
+            AppContext appContext = AppContext.getAppContext();
+            if (appContext.get(AppContext.EVENT_QUEUE_KEY) == topQueue) {
+                appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue);
             }
 
-            toPush.nextQueue = newEventQueue;
-
-            AppContext appContext = AppContext.getAppContext();
-            if (appContext.get(AppContext.EVENT_QUEUE_KEY) == toPush) {
-                appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue);
-            }
+            pushPopCond.signalAll();
         } finally {
             pushPopLock.unlock();
         }
@@ -822,44 +836,51 @@
             eventLog.fine("EventQueue.pop(" + this + ")");
         }
 
-        EventDispatchThread dt = null;
         pushPopLock.lock();
         try {
-            EventQueue toPop = this;
-            while (toPop.nextQueue != null) {
-                toPop = toPop.nextQueue;
+            EventQueue topQueue = this;
+            while (topQueue.nextQueue != null) {
+                topQueue = topQueue.nextQueue;
             }
-            EventQueue prev = toPop.previousQueue;
-            if (prev == null) {
+            EventQueue prevQueue = topQueue.previousQueue;
+            if (prevQueue == null) {
                 throw new EmptyStackException();
             }
-            toPop.previousQueue = null;
+
+            topQueue.previousQueue = null;
+            prevQueue.nextQueue = null;
 
             // Transfer all events back to previous EventQueue.
-            prev.nextQueue = null;
-            while (toPop.peekEvent() != null) {
+            while (topQueue.peekEvent() != null) {
                 try {
-                    prev.postEventPrivate(toPop.getNextEvent());
+                    prevQueue.postEventPrivate(topQueue.getNextEventPrivate());
                 } catch (InterruptedException ie) {
                     if (eventLog.isLoggable(PlatformLogger.FINE)) {
                         eventLog.fine("Interrupted pop", ie);
                     }
                 }
             }
+
+            if ((topQueue.dispatchThread != null) &&
+                (topQueue.dispatchThread.getEventQueue() == this))
+            {
+                prevQueue.dispatchThread = topQueue.dispatchThread;
+                topQueue.dispatchThread.setEventQueue(prevQueue);
+            }
+
             AppContext appContext = AppContext.getAppContext();
             if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
-                appContext.put(AppContext.EVENT_QUEUE_KEY, prev);
+                appContext.put(AppContext.EVENT_QUEUE_KEY, prevQueue);
             }
 
-            dt = toPop.dispatchThread;
+            // Wake up EDT waiting in getNextEvent(), so it can
+            // pick up a new EventQueue
+            topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
+
+            pushPopCond.signalAll();
         } finally {
             pushPopLock.unlock();
         }
-
-        if (dt != null) {
-            dt.stopDispatching(); // Must be done outside synchronized
-                                  // block to avoid possible deadlock
-        }
     }
 
     /**
@@ -907,9 +928,9 @@
         try {
             AppContext appContext = AppContext.getAppContext();
             if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
-                dispatchThread = (EventDispatchThread)
-                    AccessController.doPrivileged(new PrivilegedAction() {
-                        public Object run() {
+                dispatchThread = AccessController.doPrivileged(
+                    new PrivilegedAction<EventDispatchThread>() {
+                        public EventDispatchThread run() {
                             EventDispatchThread t =
                                 new EventDispatchThread(threadGroup,
                                                         name,
@@ -919,7 +940,8 @@
                             t.setDaemon(false);
                             return t;
                         }
-                    });
+                    }
+                );
                 AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
                 dispatchThread.start();
             }
@@ -928,7 +950,7 @@
         }
     }
 
-    final void detachDispatchThread(EventDispatchThread edt, boolean restart) {
+    final boolean detachDispatchThread(EventDispatchThread edt) {
         /*
          * This synchronized block is to secure that the event dispatch
          * thread won't die in the middle of posting a new event to the
@@ -939,26 +961,21 @@
          */
         pushPopLock.lock();
         try {
-            EventDispatchThread oldDispatchThread = dispatchThread;
-            if (dispatchThread == edt) {
-                dispatchThread = null;
-            }
-            if (restart) {
+            if (edt == dispatchThread) {
                 /*
-                 * Event dispatch thread dies in case of an uncaught exception.
-                 * A new event dispatch thread for this queue will be started
-                 * only if a new event is posted to it. In case if no more
-                 * events are posted after this thread died all events that
-                 * currently are in the queue will never be dispatched.
+                 * 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 ((peekEvent() != null) || !SunToolkit.isPostEventQueueEmpty()) {
-                    initDispatchThread();
+                    return false;
                 }
-                AWTAutoShutdown.getInstance().notifyThreadFree(oldDispatchThread);
+                dispatchThread = null;
             }
+            AWTAutoShutdown.getInstance().notifyThreadFree(edt);
+            return true;
         } finally {
             pushPopLock.unlock();
         }
--- a/src/share/classes/sun/awt/SunToolkit.java	Thu Jun 24 11:50:18 2010 +0400
+++ b/src/share/classes/sun/awt/SunToolkit.java	Tue Jul 06 17:59:56 2010 +0400
@@ -39,6 +39,7 @@
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import sun.util.logging.PlatformLogger;
 import sun.misc.SoftCache;
@@ -592,7 +593,7 @@
         }
         PostEventQueue postEventQueue =
             (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY);
-        if(postEventQueue != null) {
+        if (postEventQueue != null) {
             postEventQueue.postEvent(event);
         }
     }
@@ -610,16 +611,29 @@
         postEvent(targetToAppContext(e.getSource()), pe);
     }
 
+    private 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()  {
-        AppContext appContext = AppContext.getAppContext();
-        PostEventQueue postEventQueue =
-            (PostEventQueue)appContext.get(POST_EVENT_QUEUE_KEY);
-        if(postEventQueue != null) {
-            postEventQueue.flush();
+        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();
         }
     }
 
@@ -2079,12 +2093,14 @@
         eventQueue = eq;
     }
 
-    public boolean noEvents() {
+    public synchronized boolean noEvents() {
         return queueHead == null;
     }
 
     /*
-     * Continually post pending AWTEvents to the Java EventQueue.
+     * Continually post pending AWTEvents to the Java EventQueue. The method
+     * is synchronized to ensure the flush is completed before a new event
+     * can be posted to this queue.
      */
     public synchronized void flush() {
         EventQueueItem tempQueue = queueHead;
--- a/test/java/awt/EventDispatchThread/HandleExceptionOnEDT/HandleExceptionOnEDT.java	Thu Jun 24 11:50:18 2010 +0400
+++ b/test/java/awt/EventDispatchThread/HandleExceptionOnEDT/HandleExceptionOnEDT.java	Tue Jul 06 17:59:56 2010 +0400
@@ -1,3 +1,26 @@
+/*
+ * Copyright (c) 2010, 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.
+ *
+ * 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
   @bug 6304473 6727884
--- a/test/java/awt/EventDispatchThread/LoopRobustness/LoopRobustness.java	Thu Jun 24 11:50:18 2010 +0400
+++ b/test/java/awt/EventDispatchThread/LoopRobustness/LoopRobustness.java	Tue Jul 06 17:59:56 2010 +0400
@@ -34,35 +34,40 @@
 
 import java.awt.*;
 import java.awt.event.*;
-import java.lang.Math;
+
+import sun.awt.SunToolkit;
+
 import test.java.awt.regtesthelpers.Util;
 
 public class LoopRobustness {
-    static int clicks = 0;
+
     final static long TIMEOUT = 5000;
     final static Object LOCK = new Object();
-    static volatile boolean notifyOccur = false;
 
-    public static void main(String [] args)  {
+    public static int clicks = 0;
+    public static volatile boolean notifyOccured = false;
+    public static volatile boolean otherExceptionsCaught = false;
+
+    public static void main(String [] args) throws Exception {
         ThreadGroup mainThreadGroup = Thread.currentThread().getThreadGroup();
 
         long at;
         //wait for a TIMEOUT giving a chance to a new Thread above to accomplish its stuff.
-        synchronized (LoopRobustness.LOCK){
+        synchronized (LoopRobustness.LOCK) {
             new Thread(new TestThreadGroup(mainThreadGroup, "TestGroup"), new Impl()).start();
             at = System.currentTimeMillis();
             try {
-                while(!notifyOccur && System.currentTimeMillis() - at < TIMEOUT) {
+                while (!notifyOccured && (System.currentTimeMillis() - at < TIMEOUT)) {
                     LoopRobustness.LOCK.wait(1000);
                 }
-            } catch(InterruptedException e){
+            } catch (InterruptedException e) {
                 throw new RuntimeException("Test interrupted.", e);
             }
         }
 
-        if( !notifyOccur){
+        if (!notifyOccured) {
             //notify doesn't occur after a reasonable time.
-            throw new RuntimeException("Test failed. Second Thread didn't notify MainThread.");
+            throw new RuntimeException("Test FAILED: second thread hasn't notified MainThread");
         }
 
         //now wait for two clicks
@@ -75,7 +80,10 @@
             }
         }
         if (clicks != 2) {
-            throw new RuntimeException("robot should press button twice");
+            throw new RuntimeException("Test FAILED: robot should press button twice");
+        }
+        if (otherExceptionsCaught) {
+            throw new RuntimeException("Test FAILED: unexpected exceptions caught");
         }
     }
 }
@@ -83,18 +91,11 @@
 class Impl implements Runnable{
     static Robot robot;
     public void run() {
+        SunToolkit.createNewAppContext();
+
         Button b = new Button("Press me to test the AWT-Event Queue thread");
         Frame lr = new Frame("ROBUST FRAME");
-        /* Must load Toolkit on this thread only, rather then on Main.
-           If load on Main (on the parent ThreadGroup of current ThreadGroup) then
-           EDT will be created on Main thread and supplied with it's own exceptionHandler,
-           which just throws an Exception and terminates current thread.
-           The test implies that EDT is created on the child ThreadGroup (testThreadGroup)
-           which is supplied with its own uncaughtException().
-        */
-        Toolkit.getDefaultToolkit();
         lr.setBounds(100, 100, 300, 100);
-
         b.addActionListener(new ActionListener() {
                 public void actionPerformed(ActionEvent e) {
                     LoopRobustness.clicks++;
@@ -107,40 +108,46 @@
 
         try {
             robot = new Robot();
-        } catch(AWTException e){
+        } catch (AWTException e) {
             throw new RuntimeException("Test interrupted.", e);
         }
         Util.waitForIdle(robot);
 
         synchronized (LoopRobustness.LOCK){
             LoopRobustness.LOCK.notify();
-            LoopRobustness.notifyOccur = true;
+            LoopRobustness.notifyOccured = true;
         }
 
         int i = 0;
-        while(i < 2){
+        while (i < 2) {
             robot.mouseMove(b.getLocationOnScreen().x + b.getWidth()/2,
-                            b.getLocationOnScreen().y + b.getHeight()/2 );
+                            b.getLocationOnScreen().y + b.getHeight()/2);
+            Util.waitForIdle(robot);
             robot.mousePress(InputEvent.BUTTON1_MASK);
-            //                robot.delay(10);
+            Util.waitForIdle(robot);
             robot.mouseRelease(InputEvent.BUTTON1_MASK);
+            Util.waitForIdle(robot);
             i++;
-            robot.delay(1000);
         }
     }
 }
 
 class TestThreadGroup extends ThreadGroup {
-    TestThreadGroup(ThreadGroup threadGroup, String name){
+    TestThreadGroup(ThreadGroup threadGroup, String name) {
         super(threadGroup, name);
     }
 
-    public void uncaughtException(Thread exitedThread, Throwable e) {
-        e.printStackTrace();
-        if ((e instanceof ExceptionInInitializerError) || (e instanceof
-                NoClassDefFoundError)){
-            throw new RuntimeException("Test failed: other Exceptions were thrown ", e);
+    public void uncaughtException(Thread thread, Throwable e) {
+        System.out.println("Exception caught: " + e);
+        e.printStackTrace(System.out);
+        System.out.flush();
+        if ((e instanceof ExceptionInInitializerError) ||
+            (e instanceof NoClassDefFoundError))
+        {
+            // These two are expected
+            return;
         }
+        LoopRobustness.otherExceptionsCaught = true;
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/EventDispatchThread/PreserveDispathThread/PreserveDispatchThread.java	Tue Jul 06 17:59:56 2010 +0400
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2010, 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.
+ *
+ * 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
+  @bug 6424157
+  @author Artem Ananiev: area=eventqueue
+  @run main PreserveDispatchThread
+*/
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class PreserveDispatchThread {
+
+    private static volatile Frame f;
+    private static volatile Dialog d;
+
+    private static volatile boolean isEDT = true;
+
+    public static void main(String[] args) throws Exception {
+        f = new Frame("F");
+        f.setSize(320, 340);
+        f.setLocationRelativeTo(null);
+        f.setVisible(true);
+
+        try {
+            test1();
+            if (!isEDT) {
+                throw new RuntimeException("Test FAILED (test1): event dispatch thread is changed");
+            }
+
+            test2();
+            if (!isEDT) {
+                throw new RuntimeException("Test FAILED (test2): event dispatch thread is changed");
+            }
+
+            test3();
+            if (!isEDT) {
+                throw new RuntimeException("Test FAILED (test3): event dispatch thread is changed");
+            }
+        } finally {
+            if (d != null) {
+                d.dispose();
+            }
+            f.dispose();
+        }
+    }
+
+    /*
+     * Tests that push/pop doesn't change the dispatch thread if
+     * called on EDT.
+     */
+    private static void test1() throws Exception {
+        EventQueue.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                TestEventQueue teq = new TestEventQueue();
+                EventQueue seq = Toolkit.getDefaultToolkit().getSystemEventQueue();
+                try {
+                    seq.push(teq);
+                    d = new TestDialog();
+                    d.setVisible(true);
+                    checkEDT();
+                } finally {
+                    teq.pop();
+                }
+                checkEDT();
+            }
+        });
+    }
+
+    /*
+     * Tests that push/pop doesn't change the dispatch thread if
+     * called on the main thread.
+     */
+    private static void test2() throws Exception {
+        TestEventQueue teq = new TestEventQueue();
+        EventQueue seq = Toolkit.getDefaultToolkit().getSystemEventQueue();
+        try {
+            seq.push(teq);
+            EventQueue.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    checkEDT();
+                    d = new TestDialog();
+                    d.setVisible(true);
+                    checkEDT();
+                }
+            });
+        } finally {
+            teq.pop();
+        }
+    }
+
+    private static final Object test3Lock = new Object();
+    private static boolean test3Sync = false;
+
+    /*
+     * A complex test: several nested invokeLater() are called and
+     * in every runnable a check for EDT is performed. At the ent
+     * of the test we wait for all the runnables to be processed
+     * and the dialog is disposed; otherwise the last EDT check can
+     * be later than this method returns and the whole test is passed.
+     */
+    private static void test3() throws Exception {
+        EventQueue.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                d = new Dialog(f, true);
+                d.setSize(240, 180);
+                d.setLocationRelativeTo(f);
+                EventQueue.invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        d.setVisible(true);
+                        checkEDT();
+                    }
+                });
+                EventQueue.invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        TestEventQueue teq = new TestEventQueue();
+                        EventQueue seq = Toolkit.getDefaultToolkit().getSystemEventQueue();
+                        try {
+                            seq.push(teq);
+                            checkEDT();
+                            EventQueue.invokeLater(new Runnable() {
+                                @Override
+                                public void run() {
+                                    d.dispose();
+                                    checkEDT();
+                                    synchronized (test3Lock) {
+                                        test3Sync = true;
+                                        test3Lock.notify();
+                                    }
+                                }
+                            });
+                        } finally {
+                            teq.pop();
+                        }
+                        checkEDT();
+                    }
+                });
+                checkEDT();
+            }
+        });
+        synchronized (test3Lock) {
+            while (!test3Sync) {
+                try {
+                    test3Lock.wait();
+                } catch (InterruptedException ie) {
+                    break;
+                }
+            }
+        }
+        // Make sure all the nested invokeLater/invokeAndWait are processed
+        EventQueue.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+            }
+        });
+    }
+
+    private static void checkEDT() {
+        isEDT = isEDT && EventQueue.isDispatchThread();
+    }
+
+    private static class TestEventQueue extends EventQueue {
+        public TestEventQueue() {
+            super();
+        }
+        public void pop() {
+            super.pop();
+        }
+    }
+
+    private static class TestDialog extends Dialog {
+        private volatile boolean dialogShown = false;
+        private volatile boolean paintCalled = false;
+        public TestDialog() {
+            super(f, true);
+            setSize(240, 180);
+            setLocationRelativeTo(f);
+            addComponentListener(new ComponentAdapter() {
+                @Override
+                public void componentShown(ComponentEvent e) {
+                    if (paintCalled) {
+                        dispose();
+                    }
+                    dialogShown = true;
+                }
+            });
+        }
+        @Override
+        public void paint(Graphics g) {
+            if (dialogShown) {
+                dispose();
+            }
+            paintCalled = true;
+        }
+    }
+
+}
--- a/test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java	Thu Jun 24 11:50:18 2010 +0400
+++ b/test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java	Tue Jul 06 17:59:56 2010 +0400
@@ -43,6 +43,7 @@
         Runnable dummy = new Runnable() {
                 public void run() {
                     System.err.println("Dummy is here.");
+                    System.err.flush();
                 }
             };
         EventQueue seq = Toolkit.getDefaultToolkit().getSystemEventQueue();
@@ -58,10 +59,11 @@
         Runnable runnable = new Runnable() {
                 public void run() {
                     System.err.println("Dummy from SunToolkit");
+                    System.err.flush();
                 }
             };
         InvocationEvent ie = new InvocationEvent(eq2, runnable, null, false);
-        System.err.println(ie);
+//        System.err.println(ie);
         SunToolkit.postEvent(SunToolkit.targetToAppContext(frame), ie);
         eq1.pop();
         frame.dispose();
@@ -70,14 +72,14 @@
 
 class MyEventQueue1 extends EventQueue {
 
-    public void pop() throws EmptyStackException {
+    public void pop() {
         super.pop();
     }
 }
 
 class MyEventQueue2 extends EventQueue {
 
-    protected void pop() throws EmptyStackException {
+    protected void pop() {
         System.err.println("pop2()");
         Thread.dumpStack();
         try {
@@ -85,7 +87,8 @@
                     public void run() {
                         Runnable runnable = new Runnable() {
                                 public void run() {
-                                    System.err.println("Dummy from here");
+                                    System.err.println("Dummy from pop");
+                                    System.err.flush();
                                 }
                              };
                         InvocationEvent ie = new InvocationEvent(MyEventQueue2.this, runnable, null, false);