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"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net