class Thread | |
class TimeoutError < StandardError; end | |
def self.timeout(n) | |
temp_queue = Queue.new | |
work_thread = Thread.new { temp_queue << {return: yield} } # wrapping in a hash guarantees uniqueness of output | |
timeout_thread = Thread.new { sleep n; temp_queue << :timeout } | |
if (ret_val = temp_queue.pop) == :timeout # this pop is blocking, so it either waits on the yield or the timeout | |
work_thread.kill | |
raise TimeoutError | |
else | |
timeout_thread.kill | |
return ret_val[:return] | |
end | |
end | |
end | |
# This will either return "some val" or raise a Thread::Timeout | |
# Is raising an error a good idea? I don't know. The other | |
# options were to return nil or return a special Timeout object | |
# but I didn't like either of those, so I went with an error. | |
# It's not much trouble to wrap this in a begin, and it | |
# can be "dangerous", so I figured throwing an error was best. | |
Thread.timeout(1) do | |
sleep rand*2 # simulate work that takes 0 to 2 seconds | |
"some val" | |
end | |
# It actually feels pretty intuitive. | |
begin | |
Thread.timeout(2) do | |
(1..10000000000).reduce(:+) | |
end | |
rescue Thread::TimeoutError | |
puts "operation took longer than 2 seconds" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment