Open cpholt opened 4 years ago
I confirm this is a problem. It seems to be a known problem since CustomEventQueue_Test
reproduces it. r.settings().simpleWaitForIdle(true);
seems to be the fix. Is it the official fix? What are the drawbacks?
It brings many question. First, in this code:
Collection<EventQueue> queues = windowMonitor.allEventQueues();
if (queues.size() == 1) {
waitForIdle(checkNotNull(toolkit.getSystemEventQueue()));
return;
}
// FIXME this resurrects dead event queues
for (EventQueue queue : queues) {
waitForIdle(checkNotNull(queue));
}
Why do we need to look at anything else than the system event queue? Then, assuming this is needed, the problem is indeed, we keep looking at queues that have been pushed down the queue stack. One solution could be to check if the event is EventQueue.dummyRunnable
. If this is the case, we are in a dead queue. This is the ugly pseudo-code:
Field dummyRunnableField = EventQueue.class.getDeclaredField("dummyRunnable");
dummyRunnableField.setAccessible(true);
Runnable runnable = dummyRunnableField.get(null);
do {
// Timed out waiting for idle
if (postInvocationEvent(eventQueue, idleTimeout)) {
break;
}
// Timed out waiting for idle event queue
if (currentTimeMillis() - start > idleTimeout) {
break;
}
// Force a yield
pause();
// Look if this is a dead queue
AWTEvent event = eventQueue.peekEvent();
// Abbot: this does not detect invocation events (i.e. what gets posted with EventQueue.invokeLater), so if
// someone is repeatedly posting one, we might get stuck. Not too worried, since if a Runnable keeps calling
// invokeLater on itself, *nothing* else gets much chance to run, so it seems to be a bad programming practice.
if (event == null) {
break;
}
if (event instanceof InvocationEvent && ((InvocationEvent) event).runnable == runnable) {
// remove this queue from the list. It's a dead queue. Or just ignore and get out.
}
} while (true);
I've seen a sporadic failure where waitForIdle gets into a state where it times out after 10 seconds. The application is actually idle when this happens. This causes a lot of grief for us because it makes all the test run at a snails pace (20 minutes instead of 20 seconds).
2 important conditions for this failure:
1) install a custom eventQueue via Toolkit.getDefaultToolkit().getSystemEventQueue().push() 2) create and hide a Window first. (this is a splash screen in our real app).
After a lot of debugging by adding logs to BasicRobot waitForIdle, I manage to catch it where the custom event queue was empty, but it was waiting for a java.awt.EventQueue to drain. It contained an InvocationEvent with this runnable. "runnable=java.awt.EventQueue$1@2d1630f3". Looking into that lead me to the first anonymous class in EventQueue, which is the member variable "dummyRunnable". That dummyRunnable is used in push/pop to wake up the EDT.
I theorized that if the EDT was already awake when the new Queue was pushed, then that event could be left.
That didn't fail initially when I only had 1 window and tried the push from within an InvokeAndWait. I then expanded the test to create a window, hide it, push a new queue and then wait.
That reproduces the failure. Simple test case below. I think that the first queue (for the hidden window?) is really dead? maybe it should be removed from the "windowMonitor.allEventQueues()" in BasicRobot? I got this far, and now I'm at a loss as to how to proceed.