I\'ve been working up my answer to Is there a standard Scala function for running a block with a timeout?, and have run into a problem if an exception is thrown in a Future.
Working my way through @Rex Kerr's suggestion, I've created
object Timeout {
val timeoutException = new TimeoutException
def runWithTimeout[T](timeoutMs: Long)(f: => T) : Either[Throwable, T] = {
runWithTimeoutIgnoreExceptions(timeoutMs)(exceptionOrResult(f)) match {
case Some(x) => x
case None => Left(timeoutException)
}
}
def runWithTimeout[T](timeoutMs: Long, default: T)(f: => T) : Either[Throwable, T] = {
val defaultAsEither: Either[Throwable, T] = Right(default)
runWithTimeoutIgnoreExceptions(timeoutMs, defaultAsEither)(exceptionOrResult(f))
}
def runWithTimeoutIgnoreExceptions[T](timeoutMs: Long)(f: => T) : Option[T] = {
awaitAll(timeoutMs, future(f)).head.asInstanceOf[Option[T]]
}
def runWithTimeoutIgnoreExceptions[T](timeoutMs: Long, default: T)(f: => T) : T = {
runWithTimeoutIgnoreExceptions(timeoutMs)(f).getOrElse(default)
}
private def exceptionOrResult[T](f: => T): Either[Throwable, T] =
try {
Right(f)
} catch {
case x => Left(x)
}
}
so that
@Test def test_exception {
runWithTimeout(50) { "result" }.right.get should be ("result")
runWithTimeout(50) { throw new Exception("deliberate") }.left.get.getMessage should be ("deliberate")
runWithTimeout(50) { Thread.sleep(100); "result" }.left.get should be (Timeout.timeoutException)
runWithTimeout(50, "no result") { "result" }.right.get should be ("result")
runWithTimeout(50, "no result") { throw new Exception("deliberate") }.left.get.getMessage should be ("deliberate")
runWithTimeout(50, "no result") { Thread.sleep(100); "result" }.right.get should be ("no result")
}
Again, I'm a bit of a Scala novice, so would welcome feedback.