Domanda

Sto cercando un'interfaccia portatile per POSIX alarm(2) (o simile) in Ruby. Vale a dire, vorrei essere in grado di impostare un timer di sfondo per inviare un segnale al processo corrente dopo n secondi.

Ho trovato alcune una buona discussione dal 2006 nell'elenco ruby-talk che fornisce una soluzione usando dl/import, ma è un po 'un hack (anche se un hack pulito) e non molto portatile.

Ho esaminato il modulo molto diffamato Timeout e questo non lo taglierà sotto JRuby sebbene funzioni bene con l'interprete tradizionale. Il mio programma è una piccola shell della riga di comando che utilizza la libreria 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

In JRuby sembra che i blocchi di processo nella readline chiamata e Timeout::Error vengano lanciati solo dopo (a) il timer scade e (b) l'utente inserisce una nuova linea. E l'eccezione non viene salvata. Hmm.

Quindi ho escogitato questa soluzione alternativa:

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

Questo è ... goffo (cerchiamo di essere educati). Voglio solo <=>! Non voglio davvero trascinare nelle librerie non core (es. Terminator) per questo. C'è un modo migliore?

EDIT: Non riesco a trovare un'altra alternativa - creare un thread che dorme e quindi invia un segnale al processo - per funzionare anche con JRuby. JRuby mangia segnali? Esempio:

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

JRuby restituisce semplicemente, Ruby restituisce il " atteso; eccezione non gestita " di errore.

È stato utile?

Soluzione

Mi dispiace di non avere una risposta al tuo più grande problema di inviare un segnale dopo X secondi a un processo, ma sembra che tutto ciò che vuoi fare sia il timeout dopo X secondi di attesa per l'input, e in tal caso, direi che stai cercando Kernel.select: D

Personalmente non l'ho mai usato, ma dopo aver fatto un google per " il non-blocco ottiene " e successivamente esplorando i collegamenti, ho trovato queste due discussioni inestimabili:

http://www.ruby-forum.com/topic/126795 (Discussione sui thread multi-thread)

http://www.ruby-forum.com/topic/121404 (Spiegazione di Kernel.select nel secondo post)

Ecco un esempio di come usarlo. Questo stamperà il tuo prompt e attenderà l'input ... Se non ci sono input dopo cinque secondi, il programma finirà. Se c'è input, non appena c'è input lo sputerà indietro e finirà ... Ovviamente puoi modificarlo per i tuoi scopi.

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

Probabilmente non è così elegante come si potrebbe desiderare, ma sicuramente fa il suo lavoro senza andare in giro con i thread. Sfortunatamente, questo non ti aiuterà se desideri qualcosa di più robusto (timer / invio di un segnale al processo) e, purtroppo, non ho idea che funzioni in JRuby. Mi piacerebbe sapere se lo fa :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top