Future with timeout
In a small project I’m working on I needed a way to limit the amount of time my program waits for a future, returning a timeout if it takes too long. This functionality is not built into the Scala’s standard Future and, although you can use
scala.concurrent, this will block your thread which is not always desirable.
I came across several ways to achieve the result such as this post on Nami’s Tech Blog Scala Futures with Timeout
The solution here involves using Akka’s
akka.pattern.after which let’s you make a future that returns a specified result (succesful or otherwise) after a specified time. Unfortunately this solution requires one to pull in Akka, which is a heavy dependency if you don’t need it for anything else.
Next I found the following Stackoverflow question which has several solutions Scala Futures - built in timeout? but all of them dependencies you may not want including the Play framework and Akka.
Taking these solutions as inspiration I wrote my own that has no dependency outside the Scala and Java library. It uses a thread that sleeps for the duration of the timeout then throws an exception. By using
Future.firstCompletedOf with the timeout future and the callers future we have achieved our goal.
Well this is fine in that it works, but as noted in my comments we need to create a thread that also blocks using
Thread.sleep. Remember our initial goal was to do this without any blocking certainly with bringing an additional thread into the picture.
The next step was to determine how to make the timeout happen without starting a new thread and without any blocking. To the rescue comes java.util.Timer which we can use to trigger the timeout event in the future.
Timer has some very nice properties: It’s built into Java, it uses one thread per timer, it is thread safe and it is designed to manage thousands of active Timer events on each Timer object.
In order to use the Timer we need a
TimerTask which is a simple
Runnable object. Here’s what’s happening in the code below:
- User calls futureWithTimeout
- We create a promise with which to complete the future
- We start a timer task which will run at the timeout
- When the timeout occurs we complete the promise with
TimeoutExceptionif it is not already complete
- When the user’s future completes we succesfully (or otherwise) complete the
Promiseif it has not alread been completed
- Return the Promise’s future to the user
Here’s a small test suite showing the two cases for the users future succeeding and the users future timing out:
And finally a quick demo of this in action using Li Haoyi’s awesome Ammonite REPL