mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-23 04:24:49 +02:00
4913324: Deadlock when using two event queues
Reviewed-by: anthony, ant, dcherepanov
This commit is contained in:
parent
3336503bbb
commit
a6dd224efd
6 changed files with 358 additions and 164 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
103
jdk/test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java
Normal file
103
jdk/test/java/awt/EventQueue/PushPopDeadlock2/PushPopTest.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue