4

I am looking for possible causes for the EDT to shut down and not restart. More concrete, I have a test suite where occasionally one of the tests suffers from a time-out. The thread dump is always very similar to the following (where I stripped out some irrelevant lines from the main thread):

Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):

"Attach Listener" daemon prio=10 tid=0x00007f7c60001000 nid=0x5d0f runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"main-SharedResourceRunner" daemon prio=10 tid=0x00007f7c908e6000 nid=0x5ce6 in Object.wait() [0x00007f7c8416a000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eed580f0> (a jogamp.opengl.SharedResourceRunner)
    at java.lang.Object.wait(Object.java:503)
    at jogamp.opengl.SharedResourceRunner.run(SharedResourceRunner.java:252)
    - locked <0x00000000eed580f0> (a jogamp.opengl.SharedResourceRunner)
    at java.lang.Thread.run(Thread.java:745)

"AWT-XAWT" daemon prio=10 tid=0x00007f7c9085d000 nid=0x5ce3 runnable [0x00007f7c8456e000]
   java.lang.Thread.State: RUNNABLE
    at sun.awt.X11.XToolkit.waitForEvents(Native Method)
    at sun.awt.X11.XToolkit.run(XToolkit.java:541)
    at sun.awt.X11.XToolkit.run(XToolkit.java:505)
    at java.lang.Thread.run(Thread.java:745)

"Java2D Disposer" daemon prio=10 tid=0x00007f7c90840800 nid=0x5ce2 in Object.wait() [0x00007f7c8466f000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eee2f878> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x00000000eee2f878> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at sun.java2d.Disposer.run(Disposer.java:145)
    at java.lang.Thread.run(Thread.java:745)

"Service Thread" daemon prio=10 tid=0x00007f7c9009b800 nid=0x5cdc runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=10 tid=0x00007f7c90099800 nid=0x5cdb waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=10 tid=0x00007f7c90096800 nid=0x5cda waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x00007f7c90094000 nid=0x5cd9 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=10 tid=0x00007f7c90072000 nid=0x5cd8 in Object.wait() [0x00007f7c94e73000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eee34650> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x00000000eee34650> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" daemon prio=10 tid=0x00007f7c90070000 nid=0x5cd7 in Object.wait() [0x00007f7c94f74000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eedcc110> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:503)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
    - locked <0x00000000eedcc110> (a java.lang.ref.Reference$Lock)

"main" prio=10 tid=0x00007f7c9000c000 nid=0x5cd1 in Object.wait() [0x00007f7c99c20000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eb3adf98> (a java.awt.EventQueue$1AWTInvocationLock)
    at java.lang.Object.wait(Object.java:503)
    at java.awt.EventQueue.invokeAndWait(EventQueue.java:1282)
    - locked <0x00000000eb3adf98> (a java.awt.EventQueue$1AWTInvocationLock)
    at java.awt.EventQueue.invokeAndWait(EventQueue.java:1263)


"VM Thread" prio=10 tid=0x00007f7c9006b800 nid=0x5cd6 runnable 

"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007f7c90022000 nid=0x5cd2 runnable 

"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007f7c90023800 nid=0x5cd3 runnable 

"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007f7c90025800 nid=0x5cd4 runnable 

"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007f7c90027800 nid=0x5cd5 runnable 

"VM Periodic Task Thread" prio=10 tid=0x00007f7c900ae800 nid=0x5cdd waiting on condition 

JNI global references: 297

Note how the main thread uses EventQueue.invokeAndWait to schedule something on the EDT, but there is no EDT in the thread dump. And no EDT means the main thread will wait forever because that Runnable never terminates.

At the moment where the thread dump was taken, there were already some Runnables scheduled on the EDT, and those all ran successfully or the test wouldn't have reached this point. This suggests the EDT crashed, and could not be restarted.

Unfortunately, I have no code to reproduce the issue with. I cannot even reproduce this on my own development machine with the test that sometimes suffers from the issue. I even have a very hard time reproducing this on the CI, as it seems to time-out only once in every few hundred runs.

I am looking for suggestions what might be causing this behaviour, or what I can do to investigate this and stabilize my test. My best guess is that an exception caused the EDT to crash, but even then the EDT should recover and just be able to run the next Runnable posted to it, as illustrated in the following code:

import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;


public class Test {
  public static void main(String[] args) throws InvocationTargetException, InterruptedException {
    try {
      EventQueue.invokeAndWait(new Runnable() {
        @Override
        public void run() {
          throw new RuntimeException("exception");
        }
      });
    } catch (Exception e) {
      //ignore
    }
    EventQueue.invokeAndWait(
        new Runnable() {
          @Override
          public void run() {
            System.out.println("EDT restarted");
          }
        }
    );
    System.out.println("All runnables finished");
  }
}

Note that my test has no special UncaughtExceptionHandlers set on the EDT or on any other thread, nor had it replaced the default UncaughtExceptionHandler. I am not even sure whether there was an exception on the EDT. The CI system looses the System.err and System.out output when there is a time-out. I only have the thread dump available.


Edit

As I am afraid the question is not 100% clear. My test code looks like

@Test
public void testSomethingWhichInteractsWithTheEDT(){
  doStuffOnMainThread();
  //do or check something on the EDT
  EventQueue.invokeAndWait( new Runnable(){...});
  doMoreStuffOnMainThread();
  //do or check something else on the EDT
  EventQueue.invokeAndWait( new Runnable(){...});
}

and occasionally, one of those

EventQueue.invokeAndWait( new Runnable(){...});

calls on the main thread would block indefinitely, and the thread dump taken at that point would no longer show the "AWT-EventQueue-0" instance.

And although the test uses Swing components, they are never made visible nor are they added to a top-level component. For simplicity sake, assume I make a JPanel but never add it to a JFrame.

This means that:

  • The JVM remains alive independent whether or not the EDT is alive, until the end of my test is reached. This is because the main thread is running until the end of the test. Once my test code ends, the JVM will exit without having to dispose any of those Swing components since they were never made visible.
  • The different Runnables that I want to execute on the EDT are scheduled using invokeAndWait. In between, it is perfectly possible that the EDT shuts down as there are no visible top components and no pending events, and that the next Runnable is executed on a "new" EDT.

But as far as I can tell, this is not against any Swing related concurrency rules. I schedule all operations on the correct thread, and there is nothing mentioned in the rules that you need to keep a top-level component visible at all times during the run of your program in order to keep the same EDT alive. The fact that the EDT gets replaced (which can also happen due to an exception as discussed here) should AFAIK not block any subsequent EventQueue#invokeAndWait calls.

Community
  • 1
  • 1
Robin
  • 36,233
  • 5
  • 47
  • 99
  • good, but how, which, when is Top-Level Container at least once (really) visible ..., because without this step isn't testable somehow, everything – mKorbel Sep 22 '15 at 08:56
  • @mKorbel Not really sure what you mean, but in that particular test I have no visible top level components. I have Swing components, but never put them in a top level component nor do I make them visible – Robin Sep 22 '15 at 09:01
  • @mKorbel I tried clarifying the question, as I am still not 100% sure how your answer is related to my question – Robin Sep 22 '15 at 13:48

1 Answers1

2

@mKorbel Not really sure what you mean, but in that particular test I have no visible top level components. I have Swing components, but never put them in a top level component nor do I make them visible

@Robin

  • then isn't possible to test EDT
  • invokeAndWait should be always tested for EventQueue.isDispatchThread / SwingUtilities.isEventDispatchThread to avoids exceptions from RepaintManager
  • quite, I'm a little bit surprised by this question (is not a flamewar please:-)

for example

import java.awt.EventQueue;
import java.awt.TextField;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import javax.swing.*;

public class IsThereEDT {

    private ScheduledExecutorService scheduler;
    private AccurateScheduledRunnable periodic;
    private ScheduledFuture<?> periodicMonitor;
    private int taskPeriod = 30;
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private Date dateRun;

    public IsThereEDT() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        periodic = new AccurateScheduledRunnable() {

            private final int ALLOWED_TARDINESS = 200;
            private int countRun = 0;
            private int countCalled = 0;

            @Override
            public void run() {
                countCalled++;
                if (this.getExecutionTime() < ALLOWED_TARDINESS) {
                    countRun++;
                    isThereReallyEDT(); // non on EDT
                }
            }
        };
        periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.SECONDS);
        periodic.setThreadMonitor(periodicMonitor);
        isThereReallyEDT();
        //TextField text = new TextField();
        //JFrame frame1 = new JFrame("Frame 1");
        /*SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {                
                isThereReallyEDT();
                JFrame frame1 = new JFrame("Frame 1");
                frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame1.getContentPane().add(new JLabel("Hello in frame 1"));
                frame1.pack();
                frame1.setLocation(100, 100);
                frame1.setVisible(true);
            }
        });*/
        /*SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame2 = new JFrame("Frame 2");
                frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame2.getContentPane().add(new JLabel("Hello in frame 2"));
                frame2.pack();
                frame2.setLocation(200, 200);
                frame2.setVisible(true);
                isThereReallyEDT();
            }
        });*/
        isThereReallyEDT();
    }

    private void isThereReallyEDT() {
        dateRun = new java.util.Date();
        System.out.println("                         Time at : " + sdf.format(dateRun));
        if (EventQueue.isDispatchThread()) {
            System.out.println("Calling from EventQueue.isDispatchThread");
        } else {
            System.out.println("There isn't Live EventQueue.isDispatchThread, why any reason for that ");
        }
        if (SwingUtilities.isEventDispatchThread()) {
            System.out.println("Calling from SwingUtilities.isEventDispatchThread");
        } else {
            System.out.println("There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
          IsThereEDT isdt = new IsThereEDT();
        /*SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setVisible(true);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                }
                frame.dispose();
                JOptionPane.showMessageDialog(frame, "Test case 1, "
                        + "JVM won't terminate.");
            }
        });*/
    }
}

abstract class AccurateScheduledRunnable implements Runnable {

    private ScheduledFuture<?> thisThreadsMonitor;

    public void setThreadMonitor(ScheduledFuture<?> monitor) {
        this.thisThreadsMonitor = monitor;
    }

    protected long getExecutionTime() {
        long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
        return delay;
    }
}

with correct and good output (without showing Top-Level Container at least once (really) visible to screen)

             Time at : 11:03:50 There isn't Live EventQueue.isDispatchThread, why any reason for that  There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that 

             Time at : 11:04:20 There isn't Live EventQueue.isDispatchThread, why any reason for that  There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that 

             Time at : 11:04:50 There isn't Live EventQueue.isDispatchThread, why any reason for that  There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Not really sure that I understand this answer. Your code run as-is will of course output that it isn't on the EDT. Why would it be ? And of course, if I uncomment everything the frame remains visible and the JVM won't exit. My problem is that I call `EventQueue.invokeAndWait` from the main thread, and that it simply get stuck there because it somehow cannot start the EDT. And in my opinion this has nothing to do with visible or invisible top level components, because to make the first top-level component visible, I would have to do exactly the same call – Robin Sep 22 '15 at 10:51
  • @Robin AFAIK EventDispatchThread can be started, completed, used, then tested together with some JComponent, that must be visible, this rulles are implemented in AWT / Swing APIs, simple JFrame.setVisible(true) starting EDT, thats isn't possible to logically terminating (GC, anything == missing finalize()) iuntill current JVM instance is alive, there is many lags about usage SwingWorker, that is used without visible JComponent, otherwise you can to start EDT by dirty hack, by braeking this rulles, to override isVisible for Top-Level Container, note I'm never to seen stable testing code for – mKorbel Sep 22 '15 at 12:26
  • I realize that you need a visible top-level component to keep the EDT alive. But I do not care about it being alive or not. I just want to be able to call `EventQueue.invokeAndWait` from another thread to run something on the EDT. This should be supported, or you could never make your first top-level component visible. In my test, the JVM remains alive because my main thread is still running, so no need for the EDT to keep my JVM alive. I just want to run certain parts of the test on the EDT, because they interact with non-visible Swing components. I still do not see what I am doing wrong. – Robin Sep 22 '15 at 13:11
  • @Robin 1. seems like as you (maybe by poor description in my code:-) don't understand how EDT works - empty `queue` returns `false` from `EventQueue.isDispa...`, then 2. doesn't matter if is invokeLater called to methods implemented only in Swing APis, invokeAndWait required false, otherwise RepaintManager will hear with an exception, 3. EDT (all AWT/Swing methods, incl, SwingWorker) is designated, required (in)visible GUI, 4. EDT must be present in JVM before you want to call something to the (in)visible GUI, 5. invokeLater / invokeAndWait creates EDT together with AWT/Swing (J)Components – mKorbel Sep 22 '15 at 14:47
  • @Robin 6. you cant use invokeLater / invokeAndWait in_correctly, 7. clarifications to my point 1st. after all methods are executed to AWT/Swing and its (wrongly used) local variables are finalize()'d (prepared to GC or already GC'ed) then EventQueue switches to false from true (5-30 seconds, depends of JavaSE methods, refrain by links to unclosed FileIO, JDBC, Socked etc. quite good resolved in Java7 and newer versions) – mKorbel Sep 22 '15 at 14:51
  • 1. I see nothing in the `RepaintManager` that would throw when not on the EDT. If it would, there would never be a Swing threading violation because you would get exceptions. 2. I have invisible UI the whole time my test is running, it is simply invisible 3. I use `invokeAndWait` from the main thread != EDT, which is perfectly fine, and which should start the EDT . But perhaps I am indeed misunderstanding something, or missing some essential information. Do you perhaps have a link to some official documentation which explains why what I am doing is wrong – Robin Sep 22 '15 at 15:29