Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception causes Future to never complete

Tags:

scala

Given the following code:

import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors

val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1))

val f = Future[Unit](throw new java.lang.InternalError())(ec)

The future f never completes. f.value is always None.

There was a known bug in scala-2.10 that was fixed:

http://mandubian.com/2013/02/22/scala-future-fatal-exception

https://issues.scala-lang.org/browse/SI-7029

I am on scala-2.11.

The example from the bug report uses NotImplementedErorr, which is correctly handled by the Future and it will complete. However, if I use InternalError as in my example above, then the Future never completes. This is true whether I use the ExecutionContext.global, Executors.newSingleThreadExecutor, or Executors.newFixedThreadPool.

I can catch Throwable in the body of the Future and re-throw it with an exception wrapped around it that I know the Future will handle correctly, but that's a terrible solution.

Is this a known issue? Expected behavior? What options do I have to get sane behavior out of my Futures?

like image 597
pheaver Avatar asked Sep 17 '15 23:09

pheaver


1 Answers

From the source of Future.

override def run() = {
  promise complete {
    try Success(body) catch { case NonFatal(e) => Failure(e) }
  }
}

As you can see, Future is only catching NonFatal exception.

And the following is what NonFatal matches.

def apply(t: Throwable): Boolean = t match {
  // VirtualMachineError includes OutOfMemoryError and other fatal errors
  case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
  case _ => true

}

Since java.lang.InternalError is subclass of VirtualMachineError, the thread which is running the task is just killed by exception, and the promise will never be completed.

From the overview of the exceptions in the future

Fatal exceptions (as determined by NonFatal) are rethrown in the thread executing the failed asynchronous computation. This informs the code managing the executing threads of the problem and allows it to fail fast, if necessary. See NonFatal for a more precise description of the semantics.

Therefore I think this is intended behavior.

like image 133
ymonad Avatar answered Oct 25 '22 06:10

ymonad