A short article to talk about an interesting issue concerning Scala 2.10.0 Future that might interest you.
Summary
When a Fatal
exception is thrown in your Future
callback, it’s not caught by the Future
and is thrown to the provided ExecutionContext
.
But the current default Scala global ExecutionContext
doesn’t register an UncaughtExceptionHandler
for these fatal exceptions and your Future
just hangs forever without notifying anything to anybody.
This issue is well known and a solution to the problem has already been merged into branch 2.10.x. But this issue is present in Scala 2.10.0 so it’s interesting to keep this issue in mind IMHO. Let’s explain clearly about it.
Exceptions can be contained by Future
Let’s write some stupid code with Futures.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
You can see that a
Future
can contain anException
(or more generallyThrowable
).
Fatal Exceptions can’t be contained by Future
If you look in Scala 2.10.0 Future.scala, in the scaladoc, you can find:
1 2 3 4 |
|
and in the code, in several places, in map
or flatMap
for example, you can read:
1 2 3 4 5 |
|
This means that every
Throwable
that is Fatal can’t be contained in theFuture.Failure
.
What’s a Fatal Throwable?
To define what’s fatal, let’s see what’s declared as non-fatal in NonFatal ScalaDoc.
1 2 3 4 5 6 7 |
|
Let’s consider Fatal exceptions are just critical errors that can’t be recovered in general.
So what’s the problem?
It seems right not to catch fatal errors in the `Future, isn’t it?
But, look at following code:
1 2 3 4 5 6 |
|
Ok, the Future
doesn’t contain the Fatal Exception as expected.
But where is my Fatal Exception if it’s not caught??? No crash, notification or whatever?
There should be an `UncaughtExceptionHandler at least notifying it!
The problem is in the default Scala ExecutionContext
.
As explained in this issue, the exception is lost due to the implementation of the default global ExecutionContext
provided in Scala.
This is a simple ForkJoin pool of threads but it has no UncaughtExceptionHandler
. Have a look at code in Scala 2.10.0 ExecutionContextImpl.scala
1 2 3 4 5 6 7 8 9 10 |
|
Here it’s quite clear: there is no registered `UncaughtExceptionHandler.
What’s the consequence?
Your Future hangs forever
1 2 3 4 |
|
As you can see, you can wait as long as you want, the Future is never redeemed properly, it just hangs forever and you don’t even know that a Fatal Exception has been thrown.
As explained in the issue, please note, if you use a custom ExecutionContext
based on SingleThreadExecutor
, this issue doesn’t appear!
1 2 3 4 5 6 7 8 9 10 11 |
|
Conclusion
In Scala 2.10.0, if you have a Fatal Exception in a Future callback, your Future just trashes the Fatal Exception and hangs forever without notifying anything.
Hopefully, due to this already merged PR, in a future delivery of Scala 2.10.x, this problem should be corrected.
To finish, in the same old good issue, Viktor Klang also raised the question of what should be considered as fatal or not:
there’s a bigger topic at hand here, the one whether NotImplementedError, InterruptedException and ControlThrowable are to be considered fatal or not.
Meanwhile, be aware and take care ;)
Have Promise[NonFatal]
!