Adding a Timeout to a Long-Running Operation
Problem
You're running some code that might take a long time to complete, or might never complete at all. You want to interrupt the code if it takes too long.
Solution
Use the built-in timeout library. The Timeout.timeout method takes a code block and a deadline (in seconds). If the code block finishes running in time, it returns true. If the deadline passes and the code block is still running, Timeout.timeout terminates the code block and raises an exception.
The following code would never finish running were it not for the timeout call. But after five seconds, timeout raises a Timeout::Error and execution halts:
# This code will sleep forever… OR WILL IT? require 'timeout' before = Time.now begin status = Timeout.timeout(5) { sleep } rescue Timeout::Error puts "I only slept for #{Time.now-before} seconds." end # I only slept for 5.035492 seconds.
Discussion
Sometimes you must make a network connection or take some other action that might be incredibly slow, or that might never complete at all. With a timeout, you can impose an upper limit on how long that operation can take. If it fails, you can try it again later, or forge ahead without the information you were trying to get. Even when you can't recover, you can report your failure and gracefully exit the program, rather than sitting around forever waiting for the operation to complete.
By default, Timeout.timeout raises a Timeout::Error. You can pass in a custom exception class as the second argument to Timeout.timeout: this saves you from having to rescue the Timeout:Error just so you can raise some other error that your application knows how to handle.
If the code block had side effects, they will still be visible after the timeout kills the code block:
def count_for_five_seconds $counter = 0 begin Timeout::timeout(5) { loop { $counter += 1 } } rescue Timeout::Error puts "I can count to #{$counter} in 5 seconds." end end count_for_five_seconds # I can count to 2532825 in 5 seconds. $counter # => 2532825
This may mean that your dataset is now in an inconsistent state.
See Also
- ri Timeout
- Recipe 3.13, "Waiting a Certain Amount of Time"
- Recipe 14.1, "Grabbing the Contents of a Web Page"