Вопрос

Я ищу переносимый интерфейс для 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 выбрасывается только после (а) истечения таймера и (б) ввода пользователем новой строки.И исключение не спасается.Хм.

Итак, я придумал это обходное решение:

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).Есть ли лучший способ?

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

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

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

Это было полезно?

Решение

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

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

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

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.Хотя хотелось бы знать, так ли это :)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top