Асинхронный сигнал тревоги - PullRequest
1 голос
/ 01 февраля 2009

Я ищу портативный интерфейс для POSIX alarm(2) (или аналогичный) в Ruby. То есть я хотел бы иметь возможность установить фоновый таймер для отправки сигнала текущему процессу через n секунд.

Я нашел несколько хороших обсуждений 2006 года в списке ruby-talk, в котором предлагается решение с использованием dl/import, но это немного хак (хотя и аккуратный взлом) и не очень переносимый.

Я посмотрел на очень клеветнический модуль Timeout, и он не подрезает его под JRuby, хотя он отлично работает с традиционным интерпретатором. Моя программа представляет собой небольшую оболочку командной строки, которая использует библиотеку Readline:

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

В JRuby кажется, что блоки процесса в вызове readline и Timeout::Error генерируются только после (a) истечения таймера и (b) ввода пользователем новой строки. И исключение не будет спасено. Хм.

Итак, я нашел этот обходной путь:

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

Это ... неуклюже (давайте будем вежливы). Я просто хочу alarm(2)! Я действительно не хочу перетаскивать в неосновные библиотеки (например, Terminator) для этого. Есть ли лучший способ?

EDIT: Я не могу найти другую альтернативу - создать поток, который спит, а затем посылает сигнал процессу - также работать под JRuby. JRuby ест сигналы? Пример:

SIG = 'USR2'
Signal.trap(SIG) { raise }
Process.kill(SIG, Process.pid)

JRuby просто возвращает, Ruby возвращает ожидаемую ошибку «необработанное исключение».

1 Ответ

3 голосов
/ 02 февраля 2009

Извините, что у меня нет ответа на вашу более крупную проблему отправки сигнала через X секунд процессу, но кажется, что все, что вам нужно сделать, - это тайм-аут после X секунд ожидания ввода, и в таком случае я бы сказал, что вы ищете Kernel.select: D

Лично я никогда не использовал это, но после того, как я сделал для Google «неблокирующее получение» и впоследствии изучил ссылки, я обнаружил, что эти два вопроса являются бесценными:

http://www.ruby -forum.com / topic / 126795 (Обсуждение многопоточного получения)

http://www.ruby -forum.com / topic / 121404 (Объяснение Kernel.select во 2-м посте)

Вот пример того, как его использовать. Это распечатает ваше приглашение и будет ждать ввода ... Если через пять секунд ввода не будет, программа завершится. Если есть ввод, то как только он будет введен, он выплюнет его обратно и завершит ... Очевидно, вы можете изменить это для своих собственных целей.

def prompt
  STDOUT.write "> "
  STDOUT.flush
end

def amusing_messages
  [ "You must enter something!", 
    "Why did you even start me if you just wanted to stare at me?", 
    "Isn't there anything better you could be doing?", 
    "Just terminate me already... this is getting old",
    "I'm waiting..."]
end

prompt

loop do
  read_array, write_array, error_array = Kernel.select [STDIN], nil, nil, 5

  if read_array.nil?
    puts amusing_messages[rand(amusing_messages.length)]
  else
    puts "Result is: #{read_array[0].read_nonblock(30)}" 
  end

  prompt 

end

Это, вероятно, не так элегантно, как хотелось бы, но оно определенно справляется со своей работой, не шутя с нитями. К сожалению, это не поможет вам, если вы хотите что-то более надежное (таймер / отправка сигнала процессу), и, к сожалению, я понятия не имею, работает ли это в JRuby. Хотелось бы знать, если это так:)

...