Various sporadic problems in the Swing application I maintain appear to be caused by the way it replaces the default AWT event queue with its own custom version using Toolkit.getDefaultToolkit().getSystemEventQueue().push(new AEventQueue())
. See e.g. Threading and deadlock in Swing application. The problem described there has been resolved, but my tests (using FEST Swing) now tend to run into deadlock.
I suspect the best solution would be to replace the event queue at the beginning of the application initialization, before any Swing components are created. However, there are some dependencies that make that awkward so for the time being I am trying to find a safe way of "pushing" the new event queue after initialization, where it is currently done.
The two approaches I have tried are
- push the new queue on the EDT using
SwingUtilities.invokeLater()
; - push the new queue on the main thread after initialization, and after using
invokeLater()
to avoid deadlock with anything already started on the old EDT.
What I would expect, after reading https://stackoverflow.com/a/8965448/351885, is that the first approach might work in Java 7 but something like the second might be needed in Java 1.6. Indeed the second does work in Java 1.6, while in Java 7 both appear to complete successfully but run very very slowly. This may just be a FEST issue since the application itself seems quite responsive.
So I'm pretty much forced to use the second approach, which at least works in Java 1.6, but I would like to know
- if there is a safer way to implement this, since it seems it might be vulnerable to a race condition if an event appears on the existing queue after invokeLater
but before the new queue is created;
- if there is a different approach I should use instead.
More detail
The first "solution" looks like this:
initApplication();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new CustomEventQueue());
}
});
When compiled and run using Java 1.6, I don't understand what it is doing. It seems the thread is waiting for a lock that it already holds:
"AWT-EventQueue-1" prio=10 tid=0x00007f9808001000 nid=0x6628 in Object.wait() [0x00007f986aa72000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007d9961cf0> (a atlantis.gui.AEventQueue)
at java.lang.Object.wait(Object.java:502)
at java.awt.EventQueue.getNextEvent(EventQueue.java:490)
- locked <0x00000007d9961cf0> (a atlantis.gui.AEventQueue)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:247)
The second "solution" looks like this:
initApplication();
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
logger.debug("Waiting for AWT event queue to be empty.");
}
});
} catch (InterruptedException e) {
logger.error("Interrupted while waiting for event queue.", e);
} catch (InvocationTargetException e) {
logger.error("Error while waiting for event queue.",e);
}
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new CustomEventQueue());
As stated above, this seems to work OK in Java 1.6 but I'm not convinced it is really safe.
I haven't figured out what is happening when using Java 7, but the main thread seems to spend a long time sleeping the method org.fest.swing.timing.Pause.pause()
, which is why I suspect this may be a FEST-specific problem.