I’m looking for a portable interface to POSIX alarm(2) (or similar) in Ruby. That’s to say, I would like to be able to set a background timer to send a signal to the current process after n seconds.
I have found some good discussion from 2006 on the ruby-talk list that provides a solution using dl/import, but that’s a bit of a hack (albeit a neat hack) and not very portable.
I’ve looked at the much-maligned Timeout module and that won’t cut it under JRuby although it works fine with the traditional interpreter. My program is a small command-line shell that uses the Readline library:
TIMEOUT = 5 # seconds loop do input = nil begin Timeout.timeout(TIMEOUT) do input = Readline::readline('> ', nil) end rescue Timeout::Error puts 'Timeout' next end # do something with input end
Under JRuby it seems the process blocks in the readline call and Timeout::Error is only thrown after (a) the timer expires and (b) the user enters a new line. And the exception doesn’t get rescued. Hmm.
So I came up with this workaround:
require 'readline' class TimeoutException < Exception ; end TIMEOUT = 5 # seconds loop do input = nil start_time = Time.now thread = Thread.new { input = Readline::readline('> ', nil) } begin while thread.alive? do sleep(1) # prevent CPU from melting raise TimeoutException if(Time.now - start_time > TIMEOUT) end rescue TimeoutException thread.exit puts 'Timeout' end # do something with input end
This is… clunky (let’s be polite). I just want alarm(2)! I don’t really want to drag in non-core libraries (eg Terminator) for this. Is there a better way?
EDIT: I can’t get another alternative — creating a thread that sleeps and then sends a signal to the process — to work under JRuby either. Does JRuby eat signals? Example:
SIG = 'USR2' Signal.trap(SIG) { raise } Process.kill(SIG, Process.pid)
JRuby simply returns, Ruby returns the expected ‘unhandled exception’ error.
I’m sorry that I don’t have an answer to your larger problem of sending a signal after X seconds to a process, but it seems that all you want to do is timeout after X seconds of waiting for input, and if that’s the case then I’d say you are looking for Kernel.select 😀
I’ve personally never used this, but after doing a google for ‘non-blocking gets’, and subsequently exploring links, I found these two to be invaluable discussions:
http://www.ruby-forum.com/topic/126795 (Discussion of multi-threaded gets)
http://www.ruby-forum.com/topic/121404 (Explanation of Kernel.select in 2nd post)
Here’s a sample of how to use it. This will print out your prompt and wait for input… If there is no input after five seconds, then the program will end. If there is input, as soon as there is input it will spit it back out and end… Obviously you can modify this for your own purposes.
It’s probably not as elegant as you might like, but it definitely gets the job done without mucking around with threads. Unfortunately, this won’t help you should you want something more robust (timer/sending a signal to the process), and sadly, I have no clue if this works in JRuby. Would love to know if it does though 🙂