4913324: Deadlock when using two event queues
authorart
Fri, 27 Nov 2009 15:26:07 +0300
changeset 4365 4ac67034e98b
parent 4364 4b4427db3536
child 4366 4446f3b8a9b4
4913324: Deadlock when using two event queues Reviewed-by: anthony, ant, dcherepanov
jdk/src/share/classes/java/awt/EventDispatchThread.java
jdk/src/share/classes/java/awt/EventQueue.java
jdk/src/share/classes/sun/awt/AWTAccessor.java
jdk/src/share/classes/sun/awt/AppContext.java
jdk/src/share/classes/sun/awt/SunToolkit.java
jdk/test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java
--- a/jdk/src/share/classes/java/awt/EventDispatchThread.java	Wed Nov 25 22:14:30 2009 -0800
+++ b/jdk/src/share/classes/java/awt/EventDispatchThread.java	Fri Nov 27 15:26:07 2009 +0300
@@ -104,11 +104,8 @@
         } else {
             stopEvent.dispatch();
         }
-        synchronized (theQueue) {
-            if (theQueue.getDispatchThread() == this) {
-                theQueue.detachDispatchThread();
-            }
-        }
+
+        theQueue.detachDispatchThread(this, false);
     }
 
     public void stopDispatching() {
@@ -142,35 +139,7 @@
                 }
             });
         } finally {
-            /*
-             * 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
-             * that the event dispatch thread is busy after posting a new event
-             * to its queue, so the EventQueue.dispatchThread reference must
-             * be valid at that point.
-             */
-            synchronized (theQueue) {
-                if (theQueue.getDispatchThread() == this) {
-                    theQueue.detachDispatchThread();
-                }
-                /*
-                 * 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.
-                 */
-                /*
-                 * Fix for 4648733. Check both the associated java event
-                 * queue and the PostEventQueue.
-                 */
-                if (theQueue.peekEvent() != null ||
-                    !SunToolkit.isPostEventQueueEmpty()) {
-                    theQueue.initDispatchThread();
-                }
-                AWTAutoShutdown.getInstance().notifyThreadFree(this);
-            }
+            theQueue.detachDispatchThread(this, true);
         }
     }
 
--- a/jdk/src/share/classes/java/awt/EventQueue.java	Wed Nov 25 22:14:30 2009 -0800
+++ b/jdk/src/share/classes/java/awt/EventQueue.java	Fri Nov 27 15:26:07 2009 +0300
@@ -45,6 +45,9 @@
 import sun.awt.EventQueueItem;
 import sun.awt.AWTAccessor;
 
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
 /**
  * <code>EventQueue</code> is a platform-independent class
  * that queues events, both from the underlying peer classes
@@ -127,6 +130,14 @@
      */
     private EventQueue previousQueue;
 
+    /*
+     * A single lock to synchronize the push()/pop() and related operations with
+     * all the EventQueues from the AppContext. Synchronization on any particular
+     * event queue(s) is not enough: we should lock the whole stack.
+     */
+    private final Lock pushPopLock;
+    private final Condition pushPopCond;
+
     private EventDispatchThread dispatchThread;
 
     private final ThreadGroup threadGroup =
@@ -158,11 +169,11 @@
     static {
         AWTAccessor.setEventQueueAccessor(
             new AWTAccessor.EventQueueAccessor() {
-                public EventQueue getNextQueue(EventQueue eventQueue) {
-                    return eventQueue.nextQueue;
+                public Thread getDispatchThread(EventQueue eventQueue) {
+                    return eventQueue.getDispatchThread();
                 }
-                public Thread getDispatchThread(EventQueue eventQueue) {
-                    return eventQueue.dispatchThread;
+                public boolean isDispatchThreadImpl(EventQueue eventQueue) {
+                    return eventQueue.isDispatchThreadImpl();
                 }
             });
     }
@@ -179,6 +190,9 @@
          * may call AppContext.getAppContext() before createNewAppContext()
          * completes thus causing mess in thread group to appcontext mapping.
          */
+
+        pushPopLock = (Lock)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_LOCK_KEY);
+        pushPopCond = (Condition)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_COND_KEY);
     }
 
     /**
@@ -207,7 +221,8 @@
      */
     final void postEventPrivate(AWTEvent theEvent) {
         theEvent.isPosted = true;
-        synchronized(this) {
+        pushPopLock.lock();
+        try {
             if (dispatchThread == null && nextQueue == null) {
                 if (theEvent.getSource() == AWTAutoShutdown.getInstance()) {
                     return;
@@ -221,6 +236,8 @@
                 return;
             }
             postEvent(theEvent, getPriority(theEvent));
+        } finally {
+            pushPopLock.unlock();
         }
     }
 
@@ -280,9 +297,9 @@
                 if (theEvent.getSource() != AWTAutoShutdown.getInstance()) {
                     AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
                 }
-                notifyAll();
+                pushPopCond.signalAll();
             } else if (notifyID) {
-                notifyAll();
+                pushPopCond.signalAll();
             }
         } else {
             // The event was not coalesced or has non-Component source.
@@ -290,7 +307,7 @@
             queues[priority].tail.next = newItem;
             queues[priority].tail = newItem;
             if (notifyID) {
-                notifyAll();
+                pushPopCond.signalAll();
             }
         }
     }
@@ -482,7 +499,8 @@
              * event queues are nested with push()/pop().
              */
             SunToolkit.flushPendingEvents();
-            synchronized (this) {
+            pushPopLock.lock();
+            try {
                 for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
                     if (queues[i].head != null) {
                         EventQueueItem entry = queues[i].head;
@@ -495,7 +513,9 @@
                     }
                 }
                 AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread);
-                wait();
+                pushPopCond.await();
+            } finally {
+                pushPopLock.unlock();
             }
         } while(true);
     }
@@ -508,7 +528,8 @@
              * event queues are nested with push()/pop().
              */
             SunToolkit.flushPendingEvents();
-            synchronized (this) {
+            pushPopLock.lock();
+            try {
                 for (int i = 0; i < NUM_PRIORITIES; i++) {
                     for (EventQueueItem entry = queues[i].head, prev = null;
                          entry != null; prev = entry, entry = entry.next)
@@ -527,9 +548,11 @@
                         }
                     }
                 }
-                this.waitForID = id;
-                wait();
-                this.waitForID = 0;
+                waitForID = id;
+                pushPopCond.await();
+                waitForID = 0;
+            } finally {
+                pushPopLock.unlock();
             }
         } while(true);
     }
@@ -539,11 +562,16 @@
      * without removing it.
      * @return the first event
      */
-    public synchronized AWTEvent peekEvent() {
-        for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
-            if (queues[i].head != null) {
-                return queues[i].head.event;
+    public AWTEvent peekEvent() {
+        pushPopLock.lock();
+        try {
+            for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
+                if (queues[i].head != null) {
+                    return queues[i].head.event;
+                }
             }
+        } finally {
+            pushPopLock.unlock();
         }
 
         return null;
@@ -555,14 +583,19 @@
      * @return the first event of the specified id or <code>null</code>
      *    if there is no such event
      */
-    public synchronized AWTEvent peekEvent(int id) {
-        for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
-            EventQueueItem q = queues[i].head;
-            for (; q != null; q = q.next) {
-                if (q.event.getID() == id) {
-                    return q.event;
+    public AWTEvent peekEvent(int id) {
+        pushPopLock.lock();
+        try {
+            for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
+                EventQueueItem q = queues[i].head;
+                for (; q != null; q = q.next) {
+                    if (q.event.getID() == id) {
+                        return q.event;
+                    }
                 }
             }
+        } finally {
+            pushPopLock.unlock();
         }
 
         return null;
@@ -661,17 +694,27 @@
     public static long getMostRecentEventTime() {
         return Toolkit.getEventQueue().getMostRecentEventTimeImpl();
     }
-    private synchronized long getMostRecentEventTimeImpl() {
-        return (Thread.currentThread() == dispatchThread)
-            ? mostRecentEventTime
-            : System.currentTimeMillis();
+    private long getMostRecentEventTimeImpl() {
+        pushPopLock.lock();
+        try {
+            return (Thread.currentThread() == dispatchThread)
+                ? mostRecentEventTime
+                : System.currentTimeMillis();
+        } finally {
+            pushPopLock.unlock();
+        }
     }
 
     /**
      * @return most recent event time on all threads.
      */
-    synchronized long getMostRecentEventTimeEx() {
-        return mostRecentEventTime;
+    long getMostRecentEventTimeEx() {
+        pushPopLock.lock();
+        try {
+            return mostRecentEventTime;
+        } finally {
+            pushPopLock.unlock();
+        }
     }
 
     /**
@@ -689,10 +732,15 @@
     public static AWTEvent getCurrentEvent() {
         return Toolkit.getEventQueue().getCurrentEventImpl();
     }
-    private synchronized AWTEvent getCurrentEventImpl() {
-        return (Thread.currentThread() == dispatchThread)
-            ? ((AWTEvent)currentEvent.get())
-            : null;
+    private AWTEvent getCurrentEventImpl() {
+        pushPopLock.lock();
+        try {
+                return (Thread.currentThread() == dispatchThread)
+                ? ((AWTEvent)currentEvent.get())
+                : null;
+        } finally {
+            pushPopLock.unlock();
+        }
     }
 
     /**
@@ -706,21 +754,22 @@
      * @throws NullPointerException if <code>newEventQueue</code> is <code>null</code>
      * @since           1.2
      */
-    public synchronized void push(EventQueue newEventQueue) {
+    public void push(EventQueue newEventQueue) {
         if (eventLog.isLoggable(PlatformLogger.FINE)) {
             eventLog.fine("EventQueue.push(" + newEventQueue + ")");
         }
 
-        if (nextQueue != null) {
-            nextQueue.push(newEventQueue);
-            return;
-        }
+        pushPopLock.lock();
+        try {
+            EventQueue toPush = this;
+            while (toPush.nextQueue != null) {
+                toPush = toPush.nextQueue;
+            }
 
-        synchronized (newEventQueue) {
             // Transfer all events forward to new EventQueue.
-            while (peekEvent() != null) {
+            while (toPush.peekEvent() != null) {
                 try {
-                    newEventQueue.postEventPrivate(getNextEvent());
+                    newEventQueue.postEventPrivate(toPush.getNextEvent());
                 } catch (InterruptedException ie) {
                     if (eventLog.isLoggable(PlatformLogger.FINE)) {
                         eventLog.fine("Interrupted push", ie);
@@ -728,27 +777,30 @@
                 }
             }
 
-            newEventQueue.previousQueue = this;
-        }
-        /*
-         * 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, so if the dispatch
-         * thread attempts to synchronize on this EventQueue object
-         * it will never exit since we already hold this lock.
-         */
-        if (dispatchThread != null) {
-            dispatchThread.stopDispatchingLater();
-        }
+            newEventQueue.previousQueue = toPush;
 
-        nextQueue = newEventQueue;
+            /*
+             * 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();
+            }
 
-        AppContext appContext = AppContext.getAppContext();
-        if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
-            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);
+            }
+        } finally {
+            pushPopLock.unlock();
         }
     }
 
@@ -770,25 +822,24 @@
             eventLog.fine("EventQueue.pop(" + this + ")");
         }
 
-        // To prevent deadlock, we lock on the previous EventQueue before
-        // this one.  This uses the same locking order as everything else
-        // in EventQueue.java, so deadlock isn't possible.
-        EventQueue prev = previousQueue;
-        synchronized ((prev != null) ? prev : this) {
-          synchronized(this) {
-            if (nextQueue != null) {
-                nextQueue.pop();
-                return;
+        EventDispatchThread dt = null;
+        pushPopLock.lock();
+        try {
+            EventQueue toPop = this;
+            while (toPop.nextQueue != null) {
+                toPop = toPop.nextQueue;
             }
-            if (previousQueue == null) {
+            EventQueue prev = toPop.previousQueue;
+            if (prev == null) {
                 throw new EmptyStackException();
             }
+            toPop.previousQueue = null;
 
             // Transfer all events back to previous EventQueue.
-            previousQueue.nextQueue = null;
-            while (peekEvent() != null) {
+            prev.nextQueue = null;
+            while (toPop.peekEvent() != null) {
                 try {
-                    previousQueue.postEventPrivate(getNextEvent());
+                    prev.postEventPrivate(toPop.getNextEvent());
                 } catch (InterruptedException ie) {
                     if (eventLog.isLoggable(PlatformLogger.FINE)) {
                         eventLog.fine("Interrupted pop", ie);
@@ -797,14 +848,14 @@
             }
             AppContext appContext = AppContext.getAppContext();
             if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
-                appContext.put(AppContext.EVENT_QUEUE_KEY, previousQueue);
+                appContext.put(AppContext.EVENT_QUEUE_KEY, prev);
             }
 
-            previousQueue = null;
-          }
+            dt = toPop.dispatchThread;
+        } finally {
+            pushPopLock.unlock();
         }
 
-        EventDispatchThread dt = this.dispatchThread;
         if (dt != null) {
             dt.stopDispatching(); // Must be done outside synchronized
                                   // block to avoid possible deadlock
@@ -833,16 +884,27 @@
      */
     public static boolean isDispatchThread() {
         EventQueue eq = Toolkit.getEventQueue();
-        EventQueue next = eq.nextQueue;
-        while (next != null) {
-            eq = next;
-            next = eq.nextQueue;
+        return eq.isDispatchThreadImpl();
+    }
+
+    final boolean isDispatchThreadImpl() {
+        EventQueue eq = this;
+        pushPopLock.lock();
+        try {
+            EventQueue next = eq.nextQueue;
+            while (next != null) {
+                eq = next;
+                next = eq.nextQueue;
+            }
+            return (Thread.currentThread() == eq.dispatchThread);
+        } finally {
+            pushPopLock.unlock();
         }
-        return (Thread.currentThread() == eq.dispatchThread);
     }
 
     final void initDispatchThread() {
-        synchronized (this) {
+        pushPopLock.lock();
+        try {
             AppContext appContext = AppContext.getAppContext();
             if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
                 dispatchThread = (EventDispatchThread)
@@ -861,11 +923,45 @@
                 AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
                 dispatchThread.start();
             }
+        } finally {
+            pushPopLock.unlock();
         }
     }
 
-    final void detachDispatchThread() {
-        dispatchThread = null;
+    final void detachDispatchThread(EventDispatchThread edt, boolean restart) {
+        /*
+         * 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
+         * that the event dispatch thread is busy after posting a new event
+         * to its queue, so the EventQueue.dispatchThread reference must
+         * be valid at that point.
+         */
+        pushPopLock.lock();
+        try {
+            EventDispatchThread oldDispatchThread = dispatchThread;
+            if (dispatchThread == edt) {
+                dispatchThread = null;
+            }
+            if (restart) {
+                /*
+                 * 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.
+                 *
+                 * Fix for 4648733. Check both the associated java event
+                 * queue and the PostEventQueue.
+                 */
+                if ((peekEvent() != null) || !SunToolkit.isPostEventQueueEmpty()) {
+                    initDispatchThread();
+                }
+                AWTAutoShutdown.getInstance().notifyThreadFree(oldDispatchThread);
+            }
+        } finally {
+            pushPopLock.unlock();
+        }
     }
 
     /*
@@ -878,7 +974,12 @@
      * @see    java.awt.EventQueue#detachDispatchThread
      */
     final EventDispatchThread getDispatchThread() {
-        return dispatchThread;
+        pushPopLock.lock();
+        try {
+            return dispatchThread;
+        } finally {
+            pushPopLock.unlock();
+        }
     }
 
     /*
@@ -895,7 +996,8 @@
      */
     final void removeSourceEvents(Object source, boolean removeAllEvents) {
         SunToolkit.flushPendingEvents();
-        synchronized (this) {
+        pushPopLock.lock();
+        try {
             for (int i = 0; i < NUM_PRIORITIES; i++) {
                 EventQueueItem entry = queues[i].head;
                 EventQueueItem prev = null;
@@ -928,43 +1030,49 @@
                 }
                 queues[i].tail = prev;
             }
+        } finally {
+            pushPopLock.unlock();
         }
     }
 
     static void setCurrentEventAndMostRecentTime(AWTEvent e) {
         Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e);
     }
-    private synchronized void setCurrentEventAndMostRecentTimeImpl(AWTEvent e)
-    {
-        if (Thread.currentThread() != dispatchThread) {
-            return;
-        }
+    private void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) {
+        pushPopLock.lock();
+        try {
+            if (Thread.currentThread() != dispatchThread) {
+                return;
+            }
 
-        currentEvent = new WeakReference(e);
+            currentEvent = new WeakReference(e);
 
-        // This series of 'instanceof' checks should be replaced with a
-        // polymorphic type (for example, an interface which declares a
-        // getWhen() method). However, this would require us to make such
-        // a type public, or to place it in sun.awt. Both of these approaches
-        // have been frowned upon. So for now, we hack.
-        //
-        // In tiger, we will probably give timestamps to all events, so this
-        // will no longer be an issue.
-        long mostRecentEventTime2 = Long.MIN_VALUE;
-        if (e instanceof InputEvent) {
-            InputEvent ie = (InputEvent)e;
-            mostRecentEventTime2 = ie.getWhen();
-        } else if (e instanceof InputMethodEvent) {
-            InputMethodEvent ime = (InputMethodEvent)e;
-            mostRecentEventTime2 = ime.getWhen();
-        } else if (e instanceof ActionEvent) {
-            ActionEvent ae = (ActionEvent)e;
-            mostRecentEventTime2 = ae.getWhen();
-        } else if (e instanceof InvocationEvent) {
-            InvocationEvent ie = (InvocationEvent)e;
-            mostRecentEventTime2 = ie.getWhen();
+            // This series of 'instanceof' checks should be replaced with a
+            // polymorphic type (for example, an interface which declares a
+            // getWhen() method). However, this would require us to make such
+            // a type public, or to place it in sun.awt. Both of these approaches
+            // have been frowned upon. So for now, we hack.
+            //
+            // In tiger, we will probably give timestamps to all events, so this
+            // will no longer be an issue.
+            long mostRecentEventTime2 = Long.MIN_VALUE;
+            if (e instanceof InputEvent) {
+                InputEvent ie = (InputEvent)e;
+                mostRecentEventTime2 = ie.getWhen();
+            } else if (e instanceof InputMethodEvent) {
+                InputMethodEvent ime = (InputMethodEvent)e;
+                mostRecentEventTime2 = ime.getWhen();
+            } else if (e instanceof ActionEvent) {
+                ActionEvent ae = (ActionEvent)e;
+                mostRecentEventTime2 = ae.getWhen();
+            } else if (e instanceof InvocationEvent) {
+                InvocationEvent ie = (InvocationEvent)e;
+                mostRecentEventTime2 = ie.getWhen();
+            }
+            mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
+        } finally {
+            pushPopLock.unlock();
         }
-        mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
     }
 
     /**
@@ -1045,15 +1153,18 @@
      * or starts a new one otherwise.
      */
     private void wakeup(boolean isShutdown) {
-        synchronized(this) {
+        pushPopLock.lock();
+        try {
             if (nextQueue != null) {
                 // Forward call to the top of EventQueue stack.
                 nextQueue.wakeup(isShutdown);
             } else if (dispatchThread != null) {
-                notifyAll();
+                pushPopCond.signalAll();
             } else if (!isShutdown) {
                 initDispatchThread();
             }
+        } finally {
+            pushPopLock.unlock();
         }
     }
 }
--- a/jdk/src/share/classes/sun/awt/AWTAccessor.java	Wed Nov 25 22:14:30 2009 -0800
+++ b/jdk/src/share/classes/sun/awt/AWTAccessor.java	Fri Nov 27 15:26:07 2009 +0300
@@ -249,13 +249,13 @@
      */
     public interface EventQueueAccessor {
         /*
-         * Gets the next event queue.
-         */
-        EventQueue getNextQueue(EventQueue eventQueue);
-        /*
          * Gets the event dispatch thread.
          */
         Thread getDispatchThread(EventQueue eventQueue);
+        /*
+         * Checks if the current thread is EDT for the given EQ.
+         */
+        public boolean isDispatchThreadImpl(EventQueue eventQueue);
     }
 
     /*
--- a/jdk/src/share/classes/sun/awt/AppContext.java	Wed Nov 25 22:14:30 2009 -0800
+++ b/jdk/src/share/classes/sun/awt/AppContext.java	Fri Nov 27 15:26:07 2009 +0300
@@ -43,6 +43,9 @@
 import java.beans.PropertyChangeSupport;
 import java.beans.PropertyChangeListener;
 import sun.util.logging.PlatformLogger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * The AppContext is a table referenced by ThreadGroup which stores
@@ -132,10 +135,17 @@
     /* Since the contents of an AppContext are unique to each Java
      * session, this class should never be serialized. */
 
-    /* The key to put()/get() the Java EventQueue into/from the AppContext.
+    /*
+     * The key to put()/get() the Java EventQueue into/from the AppContext.
      */
     public static final Object EVENT_QUEUE_KEY = new StringBuffer("EventQueue");
 
+    /*
+     * The keys to store EventQueue push/pop lock and condition.
+     */
+    public final static Object EVENT_QUEUE_LOCK_KEY = new StringBuilder("EventQueue.Lock");
+    public final static Object EVENT_QUEUE_COND_KEY = new StringBuilder("EventQueue.Condition");
+
     /* A map of AppContexts, referenced by ThreadGroup.
      */
     private static final Map<ThreadGroup, AppContext> threadGroup2appContext =
@@ -244,6 +254,13 @@
                         return Thread.currentThread().getContextClassLoader();
                     }
                 });
+
+        // Initialize push/pop lock and its condition to be used by all the
+        // EventQueues within this AppContext
+        Lock eventQueuePushPopLock = new ReentrantLock();
+        put(EVENT_QUEUE_LOCK_KEY, eventQueuePushPopLock);
+        Condition eventQueuePushPopCond = eventQueuePushPopLock.newCondition();
+        put(EVENT_QUEUE_COND_KEY, eventQueuePushPopCond);
     }
 
     private static final ThreadLocal<AppContext> threadAppContext =
--- a/jdk/src/share/classes/sun/awt/SunToolkit.java	Wed Nov 25 22:14:30 2009 -0800
+++ b/jdk/src/share/classes/sun/awt/SunToolkit.java	Fri Nov 27 15:26:07 2009 +0300
@@ -722,13 +722,7 @@
         EventQueue eq = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY);
 
         AWTAccessor.EventQueueAccessor accessor = AWTAccessor.getEventQueueAccessor();
-        EventQueue next = accessor.getNextQueue(eq);
-        while (next != null) {
-            eq = next;
-            next = accessor.getNextQueue(eq);
-        }
-
-        return (Thread.currentThread() == accessor.getDispatchThread(eq));
+        return accessor.isDispatchThreadImpl(eq);
     }
 
     public Dimension getScreenSize() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java	Fri Nov 27 15:26:07 2009 +0300
@@ -0,0 +1,103 @@
+/*
+ * 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 4913324
+  @author Oleg Sukhodolsky: area=eventqueue
+  @run main/timeout=30 PushPopTest
+*/
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.EmptyStackException;
+import sun.awt.SunToolkit;
+
+public class PushPopTest {
+
+    public static Frame frame;
+    public static void main(String[] args) {
+        frame = new Frame("");
+        frame.pack();
+
+        Runnable dummy = new Runnable() {
+                public void run() {
+                    System.err.println("Dummy is here.");
+                }
+            };
+        EventQueue seq = Toolkit.getDefaultToolkit().getSystemEventQueue();
+        MyEventQueue1 eq1 = new MyEventQueue1();
+        MyEventQueue2 eq2 = new MyEventQueue2();
+        EventQueue.invokeLater(dummy);
+
+        seq.push(eq1);
+        EventQueue.invokeLater(dummy);
+
+        eq1.push(eq2);
+        EventQueue.invokeLater(dummy);
+        Runnable runnable = new Runnable() {
+                public void run() {
+                    System.err.println("Dummy from SunToolkit");
+                }
+            };
+        InvocationEvent ie = new InvocationEvent(eq2, runnable, null, false);
+        System.err.println(ie);
+        SunToolkit.postEvent(SunToolkit.targetToAppContext(frame), ie);
+        eq1.pop();
+        frame.dispose();
+    }
+}
+
+class MyEventQueue1 extends EventQueue {
+
+    public void pop() throws EmptyStackException {
+        super.pop();
+    }
+}
+
+class MyEventQueue2 extends EventQueue {
+
+    protected void pop() throws EmptyStackException {
+        System.err.println("pop2()");
+        Thread.dumpStack();
+        try {
+            EventQueue.invokeAndWait(new Runnable() {
+                    public void run() {
+                        Runnable runnable = new Runnable() {
+                                public void run() {
+                                    System.err.println("Dummy from here");
+                                }
+                             };
+                        InvocationEvent ie = new InvocationEvent(MyEventQueue2.this, runnable, null, false);
+                        SunToolkit.postEvent(SunToolkit.targetToAppContext(PushPopTest.frame), ie);
+                        postEvent(ie);
+                    }
+                });
+        } catch (InterruptedException ie) {
+            ie.printStackTrace();
+        } catch (java.lang.reflect.InvocationTargetException ie) {
+            ie.printStackTrace();
+        }
+        super.pop();
+    }
+}