4913324: Deadlock when using two event queues

Reviewed-by: anthony, ant, dcherepanov
This commit is contained in:
Artem Ananiev 2009-11-27 15:26:07 +03:00
parent 3336503bbb
commit a6dd224efd
6 changed files with 358 additions and 164 deletions

View file

@ -104,11 +104,8 @@ class EventDispatchThread extends Thread {
} else { } else {
stopEvent.dispatch(); stopEvent.dispatch();
} }
synchronized (theQueue) {
if (theQueue.getDispatchThread() == this) { theQueue.detachDispatchThread(this, false);
theQueue.detachDispatchThread();
}
}
} }
public void stopDispatching() { public void stopDispatching() {
@ -142,35 +139,7 @@ class EventDispatchThread extends Thread {
} }
}); });
} finally { } finally {
/* theQueue.detachDispatchThread(this, true);
* 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);
}
} }
} }

View file

@ -45,6 +45,9 @@ import sun.awt.SunToolkit;
import sun.awt.EventQueueItem; import sun.awt.EventQueueItem;
import sun.awt.AWTAccessor; import sun.awt.AWTAccessor;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/** /**
* <code>EventQueue</code> is a platform-independent class * <code>EventQueue</code> is a platform-independent class
* that queues events, both from the underlying peer classes * that queues events, both from the underlying peer classes
@ -127,6 +130,14 @@ public class EventQueue {
*/ */
private EventQueue previousQueue; 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 EventDispatchThread dispatchThread;
private final ThreadGroup threadGroup = private final ThreadGroup threadGroup =
@ -158,11 +169,11 @@ public class EventQueue {
static { static {
AWTAccessor.setEventQueueAccessor( AWTAccessor.setEventQueueAccessor(
new AWTAccessor.EventQueueAccessor() { new AWTAccessor.EventQueueAccessor() {
public EventQueue getNextQueue(EventQueue eventQueue) {
return eventQueue.nextQueue;
}
public Thread getDispatchThread(EventQueue eventQueue) { public Thread getDispatchThread(EventQueue eventQueue) {
return eventQueue.dispatchThread; return eventQueue.getDispatchThread();
}
public boolean isDispatchThreadImpl(EventQueue eventQueue) {
return eventQueue.isDispatchThreadImpl();
} }
}); });
} }
@ -179,6 +190,9 @@ public class EventQueue {
* may call AppContext.getAppContext() before createNewAppContext() * may call AppContext.getAppContext() before createNewAppContext()
* completes thus causing mess in thread group to appcontext mapping. * 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 @@ public class EventQueue {
*/ */
final void postEventPrivate(AWTEvent theEvent) { final void postEventPrivate(AWTEvent theEvent) {
theEvent.isPosted = true; theEvent.isPosted = true;
synchronized(this) { pushPopLock.lock();
try {
if (dispatchThread == null && nextQueue == null) { if (dispatchThread == null && nextQueue == null) {
if (theEvent.getSource() == AWTAutoShutdown.getInstance()) { if (theEvent.getSource() == AWTAutoShutdown.getInstance()) {
return; return;
@ -221,6 +236,8 @@ public class EventQueue {
return; return;
} }
postEvent(theEvent, getPriority(theEvent)); postEvent(theEvent, getPriority(theEvent));
} finally {
pushPopLock.unlock();
} }
} }
@ -280,9 +297,9 @@ public class EventQueue {
if (theEvent.getSource() != AWTAutoShutdown.getInstance()) { if (theEvent.getSource() != AWTAutoShutdown.getInstance()) {
AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
} }
notifyAll(); pushPopCond.signalAll();
} else if (notifyID) { } else if (notifyID) {
notifyAll(); pushPopCond.signalAll();
} }
} else { } else {
// The event was not coalesced or has non-Component source. // The event was not coalesced or has non-Component source.
@ -290,7 +307,7 @@ public class EventQueue {
queues[priority].tail.next = newItem; queues[priority].tail.next = newItem;
queues[priority].tail = newItem; queues[priority].tail = newItem;
if (notifyID) { if (notifyID) {
notifyAll(); pushPopCond.signalAll();
} }
} }
} }
@ -482,7 +499,8 @@ public class EventQueue {
* event queues are nested with push()/pop(). * event queues are nested with push()/pop().
*/ */
SunToolkit.flushPendingEvents(); SunToolkit.flushPendingEvents();
synchronized (this) { pushPopLock.lock();
try {
for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
if (queues[i].head != null) { if (queues[i].head != null) {
EventQueueItem entry = queues[i].head; EventQueueItem entry = queues[i].head;
@ -495,7 +513,9 @@ public class EventQueue {
} }
} }
AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread); AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread);
wait(); pushPopCond.await();
} finally {
pushPopLock.unlock();
} }
} while(true); } while(true);
} }
@ -508,7 +528,8 @@ public class EventQueue {
* event queues are nested with push()/pop(). * event queues are nested with push()/pop().
*/ */
SunToolkit.flushPendingEvents(); SunToolkit.flushPendingEvents();
synchronized (this) { pushPopLock.lock();
try {
for (int i = 0; i < NUM_PRIORITIES; i++) { for (int i = 0; i < NUM_PRIORITIES; i++) {
for (EventQueueItem entry = queues[i].head, prev = null; for (EventQueueItem entry = queues[i].head, prev = null;
entry != null; prev = entry, entry = entry.next) entry != null; prev = entry, entry = entry.next)
@ -527,9 +548,11 @@ public class EventQueue {
} }
} }
} }
this.waitForID = id; waitForID = id;
wait(); pushPopCond.await();
this.waitForID = 0; waitForID = 0;
} finally {
pushPopLock.unlock();
} }
} while(true); } while(true);
} }
@ -539,11 +562,16 @@ public class EventQueue {
* without removing it. * without removing it.
* @return the first event * @return the first event
*/ */
public synchronized AWTEvent peekEvent() { public AWTEvent peekEvent() {
for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { pushPopLock.lock();
if (queues[i].head != null) { try {
return queues[i].head.event; for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
if (queues[i].head != null) {
return queues[i].head.event;
}
} }
} finally {
pushPopLock.unlock();
} }
return null; return null;
@ -555,14 +583,19 @@ public class EventQueue {
* @return the first event of the specified id or <code>null</code> * @return the first event of the specified id or <code>null</code>
* if there is no such event * if there is no such event
*/ */
public synchronized AWTEvent peekEvent(int id) { public AWTEvent peekEvent(int id) {
for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { pushPopLock.lock();
EventQueueItem q = queues[i].head; try {
for (; q != null; q = q.next) { for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
if (q.event.getID() == id) { EventQueueItem q = queues[i].head;
return q.event; for (; q != null; q = q.next) {
if (q.event.getID() == id) {
return q.event;
}
} }
} }
} finally {
pushPopLock.unlock();
} }
return null; return null;
@ -661,17 +694,27 @@ public class EventQueue {
public static long getMostRecentEventTime() { public static long getMostRecentEventTime() {
return Toolkit.getEventQueue().getMostRecentEventTimeImpl(); return Toolkit.getEventQueue().getMostRecentEventTimeImpl();
} }
private synchronized long getMostRecentEventTimeImpl() { private long getMostRecentEventTimeImpl() {
return (Thread.currentThread() == dispatchThread) pushPopLock.lock();
? mostRecentEventTime try {
: System.currentTimeMillis(); return (Thread.currentThread() == dispatchThread)
? mostRecentEventTime
: System.currentTimeMillis();
} finally {
pushPopLock.unlock();
}
} }
/** /**
* @return most recent event time on all threads. * @return most recent event time on all threads.
*/ */
synchronized long getMostRecentEventTimeEx() { long getMostRecentEventTimeEx() {
return mostRecentEventTime; pushPopLock.lock();
try {
return mostRecentEventTime;
} finally {
pushPopLock.unlock();
}
} }
/** /**
@ -689,10 +732,15 @@ public class EventQueue {
public static AWTEvent getCurrentEvent() { public static AWTEvent getCurrentEvent() {
return Toolkit.getEventQueue().getCurrentEventImpl(); return Toolkit.getEventQueue().getCurrentEventImpl();
} }
private synchronized AWTEvent getCurrentEventImpl() { private AWTEvent getCurrentEventImpl() {
return (Thread.currentThread() == dispatchThread) pushPopLock.lock();
? ((AWTEvent)currentEvent.get()) try {
: null; return (Thread.currentThread() == dispatchThread)
? ((AWTEvent)currentEvent.get())
: null;
} finally {
pushPopLock.unlock();
}
} }
/** /**
@ -706,21 +754,22 @@ public class EventQueue {
* @throws NullPointerException if <code>newEventQueue</code> is <code>null</code> * @throws NullPointerException if <code>newEventQueue</code> is <code>null</code>
* @since 1.2 * @since 1.2
*/ */
public synchronized void push(EventQueue newEventQueue) { public void push(EventQueue newEventQueue) {
if (eventLog.isLoggable(PlatformLogger.FINE)) { if (eventLog.isLoggable(PlatformLogger.FINE)) {
eventLog.fine("EventQueue.push(" + newEventQueue + ")"); eventLog.fine("EventQueue.push(" + newEventQueue + ")");
} }
if (nextQueue != null) { pushPopLock.lock();
nextQueue.push(newEventQueue); try {
return; EventQueue toPush = this;
} while (toPush.nextQueue != null) {
toPush = toPush.nextQueue;
}
synchronized (newEventQueue) {
// Transfer all events forward to new EventQueue. // Transfer all events forward to new EventQueue.
while (peekEvent() != null) { while (toPush.peekEvent() != null) {
try { try {
newEventQueue.postEventPrivate(getNextEvent()); newEventQueue.postEventPrivate(toPush.getNextEvent());
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
if (eventLog.isLoggable(PlatformLogger.FINE)) { if (eventLog.isLoggable(PlatformLogger.FINE)) {
eventLog.fine("Interrupted push", ie); eventLog.fine("Interrupted push", ie);
@ -728,27 +777,30 @@ public class EventQueue {
} }
} }
newEventQueue.previousQueue = this; newEventQueue.previousQueue = toPush;
}
/*
* 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();
}
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(); toPush.nextQueue = newEventQueue;
if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
appContext.put(AppContext.EVENT_QUEUE_KEY, 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 @@ public class EventQueue {
eventLog.fine("EventQueue.pop(" + this + ")"); eventLog.fine("EventQueue.pop(" + this + ")");
} }
// To prevent deadlock, we lock on the previous EventQueue before EventDispatchThread dt = null;
// this one. This uses the same locking order as everything else pushPopLock.lock();
// in EventQueue.java, so deadlock isn't possible. try {
EventQueue prev = previousQueue; EventQueue toPop = this;
synchronized ((prev != null) ? prev : this) { while (toPop.nextQueue != null) {
synchronized(this) { toPop = toPop.nextQueue;
if (nextQueue != null) {
nextQueue.pop();
return;
} }
if (previousQueue == null) { EventQueue prev = toPop.previousQueue;
if (prev == null) {
throw new EmptyStackException(); throw new EmptyStackException();
} }
toPop.previousQueue = null;
// Transfer all events back to previous EventQueue. // Transfer all events back to previous EventQueue.
previousQueue.nextQueue = null; prev.nextQueue = null;
while (peekEvent() != null) { while (toPop.peekEvent() != null) {
try { try {
previousQueue.postEventPrivate(getNextEvent()); prev.postEventPrivate(toPop.getNextEvent());
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
if (eventLog.isLoggable(PlatformLogger.FINE)) { if (eventLog.isLoggable(PlatformLogger.FINE)) {
eventLog.fine("Interrupted pop", ie); eventLog.fine("Interrupted pop", ie);
@ -797,14 +848,14 @@ public class EventQueue {
} }
AppContext appContext = AppContext.getAppContext(); AppContext appContext = AppContext.getAppContext();
if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) { 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) { if (dt != null) {
dt.stopDispatching(); // Must be done outside synchronized dt.stopDispatching(); // Must be done outside synchronized
// block to avoid possible deadlock // block to avoid possible deadlock
@ -833,16 +884,27 @@ public class EventQueue {
*/ */
public static boolean isDispatchThread() { public static boolean isDispatchThread() {
EventQueue eq = Toolkit.getEventQueue(); EventQueue eq = Toolkit.getEventQueue();
EventQueue next = eq.nextQueue; return eq.isDispatchThreadImpl();
while (next != null) { }
eq = next;
next = eq.nextQueue; 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() { final void initDispatchThread() {
synchronized (this) { pushPopLock.lock();
try {
AppContext appContext = AppContext.getAppContext(); AppContext appContext = AppContext.getAppContext();
if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) { if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
dispatchThread = (EventDispatchThread) dispatchThread = (EventDispatchThread)
@ -861,11 +923,45 @@ public class EventQueue {
AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
dispatchThread.start(); dispatchThread.start();
} }
} finally {
pushPopLock.unlock();
} }
} }
final void detachDispatchThread() { final void detachDispatchThread(EventDispatchThread edt, boolean restart) {
dispatchThread = null; /*
* 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 @@ public class EventQueue {
* @see java.awt.EventQueue#detachDispatchThread * @see java.awt.EventQueue#detachDispatchThread
*/ */
final EventDispatchThread getDispatchThread() { final EventDispatchThread getDispatchThread() {
return dispatchThread; pushPopLock.lock();
try {
return dispatchThread;
} finally {
pushPopLock.unlock();
}
} }
/* /*
@ -895,7 +996,8 @@ public class EventQueue {
*/ */
final void removeSourceEvents(Object source, boolean removeAllEvents) { final void removeSourceEvents(Object source, boolean removeAllEvents) {
SunToolkit.flushPendingEvents(); SunToolkit.flushPendingEvents();
synchronized (this) { pushPopLock.lock();
try {
for (int i = 0; i < NUM_PRIORITIES; i++) { for (int i = 0; i < NUM_PRIORITIES; i++) {
EventQueueItem entry = queues[i].head; EventQueueItem entry = queues[i].head;
EventQueueItem prev = null; EventQueueItem prev = null;
@ -928,43 +1030,49 @@ public class EventQueue {
} }
queues[i].tail = prev; queues[i].tail = prev;
} }
} finally {
pushPopLock.unlock();
} }
} }
static void setCurrentEventAndMostRecentTime(AWTEvent e) { static void setCurrentEventAndMostRecentTime(AWTEvent e) {
Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e); Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e);
} }
private synchronized void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) private void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) {
{ pushPopLock.lock();
if (Thread.currentThread() != dispatchThread) { try {
return; if (Thread.currentThread() != dispatchThread) {
} return;
}
currentEvent = new WeakReference(e); currentEvent = new WeakReference(e);
// This series of 'instanceof' checks should be replaced with a // This series of 'instanceof' checks should be replaced with a
// polymorphic type (for example, an interface which declares a // polymorphic type (for example, an interface which declares a
// getWhen() method). However, this would require us to make such // getWhen() method). However, this would require us to make such
// a type public, or to place it in sun.awt. Both of these approaches // a type public, or to place it in sun.awt. Both of these approaches
// have been frowned upon. So for now, we hack. // have been frowned upon. So for now, we hack.
// //
// In tiger, we will probably give timestamps to all events, so this // In tiger, we will probably give timestamps to all events, so this
// will no longer be an issue. // will no longer be an issue.
long mostRecentEventTime2 = Long.MIN_VALUE; long mostRecentEventTime2 = Long.MIN_VALUE;
if (e instanceof InputEvent) { if (e instanceof InputEvent) {
InputEvent ie = (InputEvent)e; InputEvent ie = (InputEvent)e;
mostRecentEventTime2 = ie.getWhen(); mostRecentEventTime2 = ie.getWhen();
} else if (e instanceof InputMethodEvent) { } else if (e instanceof InputMethodEvent) {
InputMethodEvent ime = (InputMethodEvent)e; InputMethodEvent ime = (InputMethodEvent)e;
mostRecentEventTime2 = ime.getWhen(); mostRecentEventTime2 = ime.getWhen();
} else if (e instanceof ActionEvent) { } else if (e instanceof ActionEvent) {
ActionEvent ae = (ActionEvent)e; ActionEvent ae = (ActionEvent)e;
mostRecentEventTime2 = ae.getWhen(); mostRecentEventTime2 = ae.getWhen();
} else if (e instanceof InvocationEvent) { } else if (e instanceof InvocationEvent) {
InvocationEvent ie = (InvocationEvent)e; InvocationEvent ie = (InvocationEvent)e;
mostRecentEventTime2 = ie.getWhen(); mostRecentEventTime2 = ie.getWhen();
}
mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
} finally {
pushPopLock.unlock();
} }
mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
} }
/** /**
@ -1045,15 +1153,18 @@ public class EventQueue {
* or starts a new one otherwise. * or starts a new one otherwise.
*/ */
private void wakeup(boolean isShutdown) { private void wakeup(boolean isShutdown) {
synchronized(this) { pushPopLock.lock();
try {
if (nextQueue != null) { if (nextQueue != null) {
// Forward call to the top of EventQueue stack. // Forward call to the top of EventQueue stack.
nextQueue.wakeup(isShutdown); nextQueue.wakeup(isShutdown);
} else if (dispatchThread != null) { } else if (dispatchThread != null) {
notifyAll(); pushPopCond.signalAll();
} else if (!isShutdown) { } else if (!isShutdown) {
initDispatchThread(); initDispatchThread();
} }
} finally {
pushPopLock.unlock();
} }
} }
} }

View file

@ -248,14 +248,14 @@ public final class AWTAccessor {
* An accessor for the EventQueue class * An accessor for the EventQueue class
*/ */
public interface EventQueueAccessor { public interface EventQueueAccessor {
/*
* Gets the next event queue.
*/
EventQueue getNextQueue(EventQueue eventQueue);
/* /*
* Gets the event dispatch thread. * Gets the event dispatch thread.
*/ */
Thread getDispatchThread(EventQueue eventQueue); Thread getDispatchThread(EventQueue eventQueue);
/*
* Checks if the current thread is EDT for the given EQ.
*/
public boolean isDispatchThreadImpl(EventQueue eventQueue);
} }
/* /*

View file

@ -43,6 +43,9 @@ import java.util.HashSet;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import sun.util.logging.PlatformLogger; 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 * The AppContext is a table referenced by ThreadGroup which stores
@ -132,10 +135,17 @@ public final class AppContext {
/* Since the contents of an AppContext are unique to each Java /* Since the contents of an AppContext are unique to each Java
* session, this class should never be serialized. */ * 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"); 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. /* A map of AppContexts, referenced by ThreadGroup.
*/ */
private static final Map<ThreadGroup, AppContext> threadGroup2appContext = private static final Map<ThreadGroup, AppContext> threadGroup2appContext =
@ -244,6 +254,13 @@ public final class AppContext {
return Thread.currentThread().getContextClassLoader(); 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 = private static final ThreadLocal<AppContext> threadAppContext =

View file

@ -722,13 +722,7 @@ public abstract class SunToolkit extends Toolkit
EventQueue eq = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY); EventQueue eq = (EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY);
AWTAccessor.EventQueueAccessor accessor = AWTAccessor.getEventQueueAccessor(); AWTAccessor.EventQueueAccessor accessor = AWTAccessor.getEventQueueAccessor();
EventQueue next = accessor.getNextQueue(eq); return accessor.isDispatchThreadImpl(eq);
while (next != null) {
eq = next;
next = accessor.getNextQueue(eq);
}
return (Thread.currentThread() == accessor.getDispatchThread(eq));
} }
public Dimension getScreenSize() { public Dimension getScreenSize() {

View file

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