Question

I'm trying to understand what is going on with a program where I've got exceptions "disappearing" without any notice (and a thread stops working).

The simplest case I could come up with to reproduce the issue is this:

(defn -main []
  (let [a (future (do (println "Ouch... ") 
                      (/ 0 0)
                      (println "We never arrive here")))]
      (Thread/sleep 1000)
      (println "Hmmmm" @a)))

Surely enough, when run at the repl (or using lein run), I get an exception:

so.core> (-main)
Ouch... 
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:156)

Which is the behavior I expected.

However now if I remove the dereferencing of the future:

(defn -main []
  (let [a (future (do (println "Ouch... ") 
                      (/ 0 0)
                      (println "We never arrive here")))]
      (Thread/sleep 1000)
      (println "Hmmmm")))

Here's the output:

so.core> (-main)
Ouch... 
Hmmmm
nil
so.core> 

So the future is executed (otherwise "Ouch..." wouldn't be print) and obviously an exception is then thrown (otherwise "We never arrive here" would print)... But that exception is nowhere to be found and the program continues as if everything was fine.

Apparently as long as I don't try to dereference the future, the thread can silently die, in the middle of its work (in this case printing to stdout), and the exception is nowhere to be found.

Is this normal?

Am I supposed to find a (stack)trace of that exception in one of my Emacs / cider buffers?

What would be an idiomatic way to have a future doing its work (e.g. consuming messages from a queue) and yet "warn" me when something goes wrong?

Should I wrap every future call inside try/catch blocks?

Should I set a default uncaught exception handler?

Am I not supposed to run a "non-stopping" thread using a future?

Basically I'm very confused by the behaviour of Java / Clojure here and would like to have both explanation as to why it's behaving that way and how I'm supposed to deal with this issue.

Was it helpful?

Solution

In clojure future is a macro that produces a reified java.util.concurrent.Future, and dereferencing it calls the Future.get method. That method can throw an ExecutionException which represents an uncaught exception in the thread. So if you don't dereference you don't call .get and you don't get the exception.

The clojure doc site has the source, so you can see the actual implementation here.

If you don't want to dereference I suppose you should try/catch in the thread, and handle in whatever way you choose.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top