Modul Timeout
Timeout langlaufende Blöcke
Zusammenfassung
require 'timeout' status = Timeout.timeout(5) { # Something that should be interrupted if it takes more than 5 seconds... }
Beschreibung
Timeout bietet eine Möglichkeit, eine potenziell langlaufende Operation automatisch zu beenden, wenn sie nicht in einer bestimmten Zeit abgeschlossen ist.
Urheberrecht
- Urheberrecht
-
© 2000 Network Applied Communication Laboratory, Inc.
- Urheberrecht
-
© 2000 Information-technology Promotion Agency, Japan
Constants
- VERSION
-
Die Version
Öffentliche Klassenmethoden
Source
# File lib/timeout.rb, line 278 def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return yield(sec) if sec == nil or sec.zero? raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec message ||= "execution expired" if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after) return scheduler.timeout_after(sec, klass || Error, message, &block) end state = State.instance state.ensure_timeout_thread_created perform = Proc.new do |exc| request = Request.new(Thread.current, sec, exc, message) state.add_request(request) begin return yield(sec) ensure request.finished end end if klass perform.call(klass) else Error.handle_timeout(message, &perform) end end
Führt eine Operation in einem Block aus und löst eine Ausnahme aus, wenn deren Abschluss länger als sec Sekunden dauert.
sec-
Anzahl der Sekunden, die auf den Abschluss des Blocks gewartet werden soll. Jede nicht-negative Zahl oder nil kann verwendet werden, einschließlich Floats zur Angabe von Bruchteilsekunden. Ein Wert von 0 oder
nilführt den Block ohne Timeout aus. Jede negative Zahl löst eineArgumentErroraus. klass-
Exception-Klasse, die ausgelöst wird, wenn der Block nicht innerhalb vonsecSekunden abgeschlossen wird. Wenn dies weggelassen wird, wird die StandardklasseTimeout::Errorverwendet. message-
Error-Nachricht, die mit derException-Klasse ausgelöst wird. Wenn dies weggelassen wird, wird die Standardnachricht „execution expired“ verwendet.
Gibt das Ergebnis des Blocks zurück, **wenn** der Block vor Ablauf von sec Sekunden abgeschlossen wurde, andernfalls wird eine Ausnahme ausgelöst, basierend auf dem Wert von klass.
Die Ausnahme, die zum Beenden des gegebenen Blocks ausgelöst wird, ist die gegebene klass oder Timeout::ExitException, wenn klass nicht angegeben ist. Der Grund für dieses Verhalten ist, dass Timeout::Error von RuntimeError erbt und unerwartet von 'rescue' abgefangen werden könnte. Timeout::ExitException erbt von Exception, sodass es nur von `rescue Exception` abgefangen wird. Beachten Sie, dass Timeout::ExitException in eine Timeout::Error umgewandelt wird, sobald es den Aufruf Timeout.timeout erreicht, sodass es außerhalb dieses Aufrufs eine Timeout::Error ist.
Im Allgemeinen beachten Sie, dass der Codeblock die Ausnahme abfangen kann, und in diesem Fall den Timeout nicht respektiert. Außerdem kann der Block ensure verwenden, um die Behandlung der Ausnahme zu verhindern. Aus diesen Gründen kann diese Methode nicht verwendet werden, um Timeouts für nicht vertrauenswürdige Blöcke zu erzwingen.
Wenn ein Scheduler definiert ist, wird er zur Behandlung des Timeouts verwendet, indem Scheduler#timeout_after aufgerufen wird.
Beachten Sie, dass dies sowohl eine Methode des Moduls Timeout ist, sodass Sie include Timeout in Ihre Klassen einfügen können, damit diese eine timeout-Methode haben, als auch eine Modulmethode, sodass Sie sie direkt als Timeout.timeout() aufrufen können.
Sicherstellen, dass die Ausnahme nicht in ensure-Blöcken ausgelöst wird
Wenn Sie Timeout.timeout verwenden, kann es wünschenswert sein, sicherzustellen, dass die Timeout-Ausnahme nicht innerhalb eines ensure-Blocks ausgelöst wird. Der einfachste und beste Weg, dies zu tun, ist, den Timeout.timeout-Aufruf in den Körper des begin/ensure/end zu setzen.
begin Timeout.timeout(sec) { some_long_operation } ensure cleanup # safe, cannot be interrupt by timeout end
Wenn dies nicht möglich ist, z. B. wenn es ensure-Blöcke innerhalb von some_long_operation gibt, müssen diese nicht durch Timeout unterbrochen werden, und es ist nicht möglich, diese ensure-Blöcke nach außen zu verschieben. Sie können Thread.handle_interrupt verwenden, um die Timeout-Ausnahme zu verzögern, wie folgt:
Thread.handle_interrupt(Timeout::Error => :never) { Timeout.timeout(sec, Timeout::Error) do setup # timeout cannot happen here, no matter how long it takes Thread.handle_interrupt(Timeout::Error => :immediate) { some_long_operation # timeout can happen here } ensure cleanup # timeout cannot happen here, no matter how long it takes end }
Wichtig ist zu beachten, dass eine Ausnahme-Klasse an Timeout.timeout übergeben werden muss, sonst funktioniert es nicht. Insbesondere die Verwendung von +Thread.handle_interrupt(Timeout::ExitException => ...)+ wird nicht unterstützt und verursacht subtile Fehler, wie z. B. die Auslösung der falschen Ausnahme außerhalb des Blocks. Verwenden Sie dies nicht.
Beachten Sie, dass Thread.handle_interrupt etwas gefährlich ist, da, wenn die Einrichtung oder Bereinigung hängt, der aktuelle Thread ebenfalls hängen bleibt und der Timeout niemals ausgelöst wird. Beachten Sie auch, dass der Block länger als sec Sekunden laufen kann: z. B. führt some_long_operation sec Sekunden + die Bereinigungszeit aus.
Wenn der Timeout nur bei blockierenden Operationen erfolgen soll, kann man anstelle von :immediate :on_blocking verwenden. Das bedeutet jedoch, dass, wenn der Block nach sec Sekunden keine blockierenden Operationen mehr verwendet, der Block nicht unterbrochen wird.
Private Instanzmethoden
Source
# File lib/timeout.rb, line 308 def timeout(*args, &block) Timeout.timeout(*args, &block) end