6

In my program I made an assert - which evaluates to false - from a runnable, but never see any console output about the assert. I want to know if my asserts are false, but it seems the runnable is catching all the thrown asserts?

Below is the simplest example program I could write to demonstrate. (Assertions are enabled. The program would behave differently if they weren't and print both lines instead of just the one). The output of the program is.

About to assert False

That's it. After that the assert statement throws and is caught by something and I never know about it. I want to know about it, what am I doing wrong?

import java.nio.ByteBuffer;
import java.util.concurrent.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.awt.FlowLayout;
import javax.swing.*;

class App
{
  private static final ScheduledExecutorService sExecutor =
    Executors.newSingleThreadScheduledExecutor();

  // Main
  public static void main(String[] args)
  {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() { createAndShowGUI(); } });

  }

  // Swing GUI
  private static void createAndShowGUI()
  {
    // Just create a swing thing. Boring
    JFrame frame = new JFrame("Title String");
    JLabel label = new JLabel("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(label);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.pack();
    frame.setVisible(true);

    // ********************************************
    // INTERESTING CODE HERE. We schedule a runnable which assert's false
    // but we never see a console assert error!
    // ********************************************
    sExecutor.schedule(new Runnable()
      { @Override public void run() { doAssertFalse(); }}, 0, TimeUnit.SECONDS);

  }

  public static void doAssertFalse()
  {
    System.out.println("About to assert False");
    assert false;
    System.out.println("Done asserting False");
  }
}
Dijkstra
  • 323
  • 1
  • 9
  • Have you enabled the assertion on the command line when launching your application? – Laf Jan 15 '13 at 19:16
  • Assertions are enabled. The program would behave differently if the were not (it would print both lines instead of just the one) – Dijkstra Jan 15 '13 at 19:19
  • 1
    Maybe it has to do with the `UncaughtExceptionHandler` associated with your thread. Have you tried setting a custom one, to see if you can see the assertion exception pass by? – Laf Jan 15 '13 at 19:24
  • @Laf: How do you do that with an executor? The executor creates the `Thread`s for you without giving you access to them (afaik) and you need to put an `UncaughtExceptionHandler` on the `Thread` instance. – Daniel Kaplan Jan 15 '13 at 20:05
  • 1
    @DanielKaplan I haven't really tested it, so I might be totally wrong, but a `Thread` is a `Runnable`, so you could create your own `Thread` implementation with your code, and associate a custom `UncaughtExceptionHandler` to this thread, then use it as a parameter when calling the executor. Don't know if it would make a difference. In any case, you have provided an answer which is probably way more efficient than my suggestion. – Laf Jan 15 '13 at 20:11

2 Answers2

3

This will do it:

private static final ScheduledExecutorService sExecutor =
        Executors.newSingleThreadScheduledExecutor();

// Main
public static void main(String[] args)
{
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            try {
                createAndShowGUI();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } });

}

// Swing GUI
private static void createAndShowGUI() throws ExecutionException, InterruptedException {
    // Just create a swing thing. Boring
    JFrame frame = new JFrame("Title String");
    JLabel label = new JLabel("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(label);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.pack();
    frame.setVisible(true);

    // ********************************************
    // INTERESTING CODE HERE. We schedule a runnable which assert's false
    // but we never see a console assert error!
    // ********************************************
    ScheduledFuture<?> future = sExecutor.schedule(new Runnable() {
        @Override
        public void run() {
            doAssertFalse();
        }
    }, 0, TimeUnit.SECONDS);
    future.get();

}

public static void doAssertFalse()
{
    System.out.println("About to assert False");
    assert false;
    System.out.println("Done asserting False");
}

Notice I'm saving the result of schedule into a ScheduledFuture variable. The exception isn't returned until you call the get() method on the future. All exceptions are thrown wrapped in an ExecutionException.

Unfortunately this blocks, so another way you can get the exception is like this:

// Swing GUI
private static void createAndShowGUI() {
    // Just create a swing thing. Boring
    JFrame frame = new JFrame("Title String");
    JLabel label = new JLabel("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(label);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.pack();
    frame.setVisible(true);

    // ********************************************
    // INTERESTING CODE HERE. We schedule a runnable which assert's false
    // but we never see a console assert error!
    // ********************************************
    sExecutor.schedule(new Runnable() {
        @Override
        public void run() {
            try {
                doAssertFalse();
            } catch (Error e) {
                e.printStackTrace();
            }
        }
    }, 0, TimeUnit.SECONDS);

}

public static void doAssertFalse() {
    System.out.println("About to assert False");
    assert false;
    System.out.println("Done asserting False");
}

Notice that I'm catching an Error, not an Exception. I'm doing this because Assertions throw a java.lang.AssertionError, not a *Exception.

I'm having trouble finding any documentation in the Javadoc saying that the ScheduledExecutorService swallows exceptions unless you do these things, but through my tests that appears to be the case.

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • That's true, the exception thrown by the assert will be caught and printed. But I was under the impression that you could add asserts so you the programmer can know something went wrong. Do I have to add a try catch block around every assert (or asserting method) I write? – Dijkstra Jan 15 '13 at 19:31
  • 1
    Technically, it's not the *exception*, but the *error* that's thrown. That's why you're having issues. – Daniel Kaplan Jan 15 '13 at 19:32
  • My thought process is "Hm I want to make sure numApples is never negative so I'll add an assert!" assert numApples >= 0. But sometimes when the assert is false I am not informed. Must a try catch block go with every assert? – Dijkstra Jan 15 '13 at 19:35
  • I consider this a workaround, and not an answer to the question. – Bohemian Jan 15 '13 at 19:36
  • @Bohemian Why do you say that? – Daniel Kaplan Jan 15 '13 at 19:42
  • I couldn't find anything in the docs either. So I looked at the FutureTask::innerRun() code which seems to be running it. It catches all throwables, then sets an inner exception. Assertions are a throwable so they are caught... – Dijkstra Jan 15 '13 at 19:51
  • This [question](http://stackoverflow.com/questions/1957645/when-to-use-assertion-vs-exception) has some good insight on when to use assertion vs using exceptions. – Laf Jan 15 '13 at 20:00
  • @DanielKaplan That makes sense about the ScheduledFuture. But now I'm sad because I can't put asserts in my code and expect that they will always inform me of errors. – Dijkstra Jan 15 '13 at 20:06
  • @Dijkstra Well if you follow my advice you'll always be informed. It's just kind of annoying that you have to remember to follow my advice instead of it happening automatically :( That being said, relating this to assertions is a little narrow: This same issue occurs with Runtime exceptions. I guess you've just got to be careful with the `schedule` method. – Daniel Kaplan Jan 15 '13 at 20:08
0

Threads don't do anything with exceptions (How could they? What if you don't use STDERR or STDOUT but log to a database, how could Thread.run() know where to log the exception or who to rethrow it to?)

There is a Thread.UncaughtExceptionHandler that you can use, and when you do so your Asserts should work through that.

I suppose the problem is that the mechanism for Assert is using exceptions and therefore must follow the predetermined rules for exceptions. Personally I wouldn't mind if Assert simply printed a stack dump and did an exit(0) because you shouldn't ever be catching that exception anyway--but it is what it is.

Note you can also use Thread.setDefaultUncaughtExceptionHandler just once for all threads in your app (Notice the "DEFAULT")... this should be a pretty good general solution.

import java.lang.*;

public class ThreadDemo {

   public static void main(String[] args) {

     Thread t = new Thread(new adminThread());

     t.setUncaughtExceptionHandler(new Thread.
     UncaughtExceptionHandler() {
        public void uncaughtException(Thread t2, Throwable e) {
           System.out.println(t2 + " throws exception: " + e);
        }
     });
     // this will call run() function
     t.start();

   }
}

class adminThread implements Runnable {

   public void run() {
      assert(false);
      throw new RuntimeException();
   }
} 

When I run this "Normally" it shows the RuntimeException was thrown. When I run with the "-ea" option it shows an AssertException. Give it a try.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • This all sounds like it should work, but when I tried it it didn't. I wish there was a way I could paste a code block into a comment to give more detail. I implemented the first line of main() to call `Thread.setDefaultUncaughtExceptionHandler` and the impl just says `e.printStackTrace();`. Still no output to the console though. – Daniel Kaplan Jan 15 '13 at 23:04
  • In addition, if I set a breakpoint on that `e.printStackTrace();` it doesn't hit it. – Daniel Kaplan Jan 15 '13 at 23:05
  • That's kind of a "strawman" answer. In his scenario he's using a scheduled executor. In this case, it makes all the difference. – Daniel Kaplan Jan 16 '13 at 19:59
  • In that case, the answer here will get you the execption: http://stackoverflow.com/a/2554845/12943 – Bill K Jan 16 '13 at 20:57
  • Yes, that's a similar scenario. I addressed this solution in my answer. – Daniel Kaplan Jan 16 '13 at 21:39