Duncan McGregor
Duncan McGregor

Reputation: 18167

Is there a standard Scala function for running a block with a timeout?

I need to call into a service that may or not return timely results. I'd like to be able to write

val result = runWithTimeout(5000, valReturnedOnTimeout) { service.fetch }

Is there a standard function that will do the job - like Ruby's timeout?

Upvotes: 10

Views: 9221

Answers (6)

Duncan McGregor
Duncan McGregor

Reputation: 18167

With credit to the other answers - in the absence of any standard library function, I've gone down the Futures route.

  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent._
  import scala.concurrent.duration._

  def runWithTimeout[T](timeoutMs: Long)(f: => T) : Option[T] = {
    Some(Await.result(Future(f), timeoutMs milliseconds))
  }

  def runWithTimeout[T](timeoutMs: Long, default: T)(f: => T) : T = {
    runWithTimeout(timeoutMs)(f).getOrElse(default)
  }

So that

  @Test def test {
    runWithTimeout(50) { "result" } should equal (Some("result"))
    runWithTimeout(50) { Thread.sleep(100); "result" } should equal (None)
    runWithTimeout(50, "no result") { "result" } should equal ("result")
    runWithTimeout(50, "no result") { Thread.sleep(100); "result" } should equal("no result")
  }

I'd be grateful for any feedback as to whether this is a good Scala style!

Upvotes: 10

Qnob
Qnob

Reputation: 31

The post above

  import scala.concurrent.ExecutionContext.Implicits.global   import
 scala.concurrent._   import scala.concurrent.duration._

   def runWithTimeout[T](timeoutMs: Long)(f: => T) : Option[T] = {
     Await.result(Future(f), timeoutMs milliseconds).asInstanceOf[Option[T]]   }

   def runWithTimeout[T](timeoutMs: Long, default: T)(f: => T) : T = {
     runWithTimeout(timeoutMs)(f).getOrElse(default)   }

didn't work for me on Scala 2.11.

Following modified version works for me:

  def runWithTimeout[T](timeout: Long)(f: => T): Option[T] = {
    Option.apply(Await.result(Future(f), timeout seconds))
  }

Upvotes: 0

Don Mackenzie
Don Mackenzie

Reputation: 7963

Something that hasn't been mentioned yet is awaitEither, a method on the actors package's Futures object. awaitEither returns the result from the first of a pair of futures to complete, so for example something like this could be used:

awaitEither(future{task}, alarm(timeoutPeriod))

and then dressed up in a method as suggested:

def runWithTimeout[T](timeoutPeriod: Int, timeoutValue: T)(task: => T) = {
  awaitEither(future{task}, alarm(timeoutPeriod)) match {case () => timeoutValue case x => x}
}

alarm returns Unit which is assignable to a val of type Any so awaitEither returns something that can be pattern matched against.

Upvotes: 2

oluies
oluies

Reputation: 17831

You could use a future

import scala.actors.Futures._  

val myfuture = 
    future {
     Thread.sleep(5000)
     println("<future>")
     "future "
 }

 awaitAll(300,myfuture ) foreach println _   

But also have a look at Circuit Breaker for Scala which is a implementation of the Circuit Breaker Pattern. Basically it lets you control the timeout and what should happen if a failure occurs accessing an external resource

Usage looks like this in Scala (from the readme) :

. . .
addCircuitBreaker("test", CircuitBreakerConfiguration(timeout=100,failureThreshold=10))
. . .


class Test extends UsingCircuitBreaker {
  def myMethodWorkingFine = {
    withCircuitBreaker("test") {
      . . .
    }
  }

  def myMethodDoingWrong = {
    withCircuitBreaker("test") {
      require(false,"FUBAR!!!")
    }
  }
}

Upvotes: 5

pr1001
pr1001

Reputation: 21962

Might Futures and its alarm do the trick?

Upvotes: 2

Kim Stebel
Kim Stebel

Reputation: 42047

You can start it in a new Thread and then wait for it to finish with Thread.join. If you pass a parameter to join, it waits at most that many milliseconds.

val t = new Thread {
  override def run() {
    //...
  }
}
t.start()
t.join(5000)

Upvotes: 1

Related Questions