user7938511
user7938511

Reputation: 167

Scala: How to catch exceptions in child threads

I had thought that the Try catches cross-Thread exceptions as in example below. I guess not: so how do I catch exceptions in spawned child threads?

// Simple class that throws error
class Child extends Runnable {
  def run {
    val exception: Exception = new Exception("Foo")
    val i = 1
    Thread.sleep(1000)
    val lines = scala.io.Source.fromFile("/tmp/filefoobar.txt").mkString
    Thread.sleep(1000)
  }
}
// spawn the class above
def Parent() = {
  val doit = Try {
    val t = new Thread(new Child)
    t.start
    t.join()
  }

  doit match {
    case Success(v) => println("uh oh did not capture error")
    case Failure(v) => println("good we caught the error")
  }
}

Output scala> Parent()

Exception in thread "Thread-35" java.io.FileNotFoundException: /tmp/filefoobar.txt (No such file or directory)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at scala.io.Source$.fromFile(Source.scala:91)
    at scala.io.Source$.fromFile(Source.scala:76)
    at scala.io.Source$.fromFile(Source.scala:54)
    at $line120.$read$$iw$$iw$Child.run(<console>:16)
    at java.lang.Thread.run(Thread.java:745)
uh oh did not capture error

Upvotes: 5

Views: 1755

Answers (3)

simpadjo
simpadjo

Reputation: 4017

Consider using Futures to handle result of async task

import ExecutionContext.Implicits.global
val resultFuture: Future[Unit] = Future { new Child.run }
resultFuture.onComplete (result: Try[Unit] => ...)

Upvotes: 6

Matt Fowler
Matt Fowler

Reputation: 2733

You can use Thread.setUncaughtExceptionHandler to set the exception and then rethrow it from the try:

import java.lang.Thread.UncaughtExceptionHandler

def Parent() = {

  @volatile
  var maybeException: Option[Throwable] = None

  val doit = Try {
    val target = new Child
    val t = new Thread(target)
    t.setUncaughtExceptionHandler(new UncaughtExceptionHandler {
      override def uncaughtException(t: Thread, th: Throwable): Unit = {
        maybeException = Some(th)
      }
    })
    t.start()
    t.join()

    maybeException.foreach(th => throw th)
  }

  doit match {
    case Success(v) => println("uh oh did not capture error")
    case Failure(v) => println("good we caught the error")
  }
}

Upvotes: 0

Hellzzar
Hellzzar

Reputation: 195

When the child accesses to the file raises an exception which captures JRE and follows the default behaviour; print stack trace, then propagates exception to parent processes, which captures the Parent as you can see in your output.

What you could do is catching the exception in the child process and throw yourself the exception so the parent could handle it.

Upvotes: 0

Related Questions