简体   繁体   中英

Why does my Scala future not propagate an Error?

This code works as expected:

it("should propagate exceptions") {
  intercept[RuntimeException] {
    val future = Future { Thread.sleep(10); sys.error("whoops"); 22 }
    Await.result(future, Duration.Inf)
  }.getMessage should equal ("whoops")
}

But this doesn't:

it("should propagate errors") {
  intercept[StackOverflowError] {
    val future = Future { Thread.sleep(10); throw new StackOverflowError("dang"); 22 }
    Await.result(future, Duration.Inf)
  }.getMessage should equal ("dang")
}

The future in this second test never returns. Why doesn't an Error subclass (as opposed to an Exception subclass) terminate my future? How should I handle Error s?

EDIT: This is possibly related, but not identical, to Why does Scala Try not catching java.lang.StackOverflowError? . I'm not using Try here. The core issue is that the Future never returns at all; I can't catch any error from it because it just hangs.

As pointed out in the comments, this is a duplicate of Why does Scala Try not catching java.lang.StackOverflowError?

According to Scala documentation.

Note: only non-fatal exceptions are caught by the combinators on Try (see >scala.util.control.NonFatal). Serious system errors, on the other hand, will be >thrown.

No Throwable -> Errors are catched by Try

Also to answer your question about how error handling is usually done. In Scala you can use try / catch for code that can cause exceptions (very similar to Java):

try { 
// ... Your dangerous code in here
} catch {
 case ioe: IOException => ... //
 case e: Exception => ...
} 

And you should always have the more specific exceptions first.

The code you provided would look something like this: https://scastie.scala-lang.org/2DJXJ6ESS9ySJZSwSodmZg

Also I tried out your code and it definitely produces the StackOverFlowerror. But it can't catch it properly like the above mentioned link explains.

The reporter facility is for catastrophes, which just hooks into the thread's UncaughtExceptionHandler , but it looks like it works out of the box with just the default thread factory:

scala 2.13.0-M5> import concurrent._,java.util.concurrent.Executors
import concurrent._
import java.util.concurrent.Executors

scala 2.13.0-M5> val ec = ExecutionContext.fromExecutor(null, e => println(s"Handle: $e"))
ec: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl$$anon$3@5e7c141d[Running, parallelism = 4, size = 0, active = 0, running = 0, steals = 0, tasks = 0, submissions = 0]

scala 2.13.0-M5> val f = Future[Int](throw new NullPointerException)(ec)
f: scala.concurrent.Future[Int] = Future(<not completed>)

scala 2.13.0-M5> f
res0: scala.concurrent.Future[Int] = Future(Failure(java.lang.NullPointerException))

scala 2.13.0-M5> val f = Future[Int](throw new StackOverflowError)(ec)
Handle: java.lang.StackOverflowError
f: scala.concurrent.Future[Int] = Future(<not completed>)

whereas

scala 2.13.0-M5> val ec = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor, e => println(s"Handle: $e"))
ec: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@317a118b

scala 2.13.0-M5> val f = Future[Int](throw new StackOverflowError)(ec)
f: scala.concurrent.Future[Int] = Future(<not completed>)
Exception in thread "pool-1-thread-1" java.lang.StackOverflowError
    at $line14.$read$$iw$$iw$$iw$$iw$.$anonfun$f$1(<console>:1)
    at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:659)
    at scala.util.Success.$anonfun$map$1(Try.scala:261)
    at scala.util.Success.map(Try.scala:209)
    at scala.concurrent.impl.Promise$Transformation.doMap(Promise.scala:420)
    at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:402)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

You could construct a rig that registers a future when it runs, and a safe await that knows when threads have blown up. Maybe you want to retry an algorithm with a lower max recursion depth, for example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM