Timeout auf Aufrufprozess / Shell-Befehl anwenden?

7

In einigen Fällen ist es beim Delegieren von Arbeit an einen externen Prozess hilfreich, eine Zeitüberschreitung für den Befehl festzulegen, um zu verhindern, dass Emacs auf unbestimmte Zeit hängen bleibt.
Leider funktioniert das Folgende nicht.

(with-timeout (1 nil)
  (call-process "/usr/bin/bash" nil t nil "-c" "sleep 10"))

Das gilt auch für shell-command.

Gibt es eine Möglichkeit, ein Timeout für diese synchronen Prozesse festzulegen?
Das heißt, ich möchte, dass der Prozess automatisch abgebrochen wird, wenn er nicht innerhalb einer bestimmten Anzahl von Sekunden abgeschlossen ist. Ist das möglich?

Malabarba
quelle
Ich habe noch nie benutzt with-timeout, aber ich habe kill-processund benutzt delete-process.
Lawlist
Ich bin mir nicht sicher, aber vielleicht würde es helfen, die Prozessausgabe zu akzeptieren. Das heißt, es würde Emacs anweisen, die Initiative zu ergreifen und einen Elisp-Code auszuführen, der angeblich darauf wartet, dass der Prozess eine Ausgabe erzeugt. Vielleicht wäre dies auch ein guter Zeitpunkt, um den Prozess zu beenden, wenn das Zeitlimit abgelaufen ist.
wvxvw
@wvxvw call-processOben wird festgelegt, dass die Ausgabe auf dem aktuellen Puffer gedruckt werden soll (ich erhalte den gleichen Effekt, wenn ich einen anderen Ausgabepuffer übergebe). Meinst Du das?
Malabarba
Nein, tut mir leid, ich habe einige Dinge verwirrt. Ich meinte dies: gnu.org/software/emacs/manual/html_node/elisp/…, aber dies ist nur für die asynchronen Prozesse relevant. Wenn ich zu Hause bin, werde ich in die call-processQuellen schauen , aber jetzt beginne ich zu vermuten, dass es keine Möglichkeit gibt, es bei einer Auszeit zu töten.
wvxvw
Eigentlich habe ich versucht, es auf Github nachzuschlagen: github.com/emacs-mirror/emacs/blob/… und nein, ich sehe dort keinen Code, der so etwas wie das Festlegen eines Zeitlimits für den Prozess bewirken würde.
wvxvw

Antworten:

4

Während der call-processAusführung verarbeitet Emacs Ereignisse und with-timeoutfunktioniert ohne diese Funktion nicht:

Das Zeitlimit wird immer dann überprüft, wenn Emacs auf ein externes Ereignis wartet (z. B. Tastatureingabe, Eingabe von Unterprozessen oder eine bestimmte Zeit). Wenn das Programm ohne Wartezeit wiederholt wird, wird das Zeitlimit nicht erkannt.

Sie können weiterhin with-timeoutmit (halb) synchronen Prozessen arbeiten.

Sie verwenden tatsächlich einen asynchronen Prozess, warten jedoch synchron, während er ausgeführt wird. Emacs verarbeitet beim Ausführen Ereignisse sit-for, die Sie 0 Sekunden lang ausführen können. Sie können dann das Argument timeout-forms von verwenden with-timeout, um den Prozess abzubrechen, wenn er beim Auslösen des Timeouts noch ausgeführt wird.

(with-current-buffer (get-buffer-create "*my-proc-buffer*")
  (let ((proc (start-process "myproc" (current-buffer) "bash" "-c" "sleep 4"))) ;; start an async process
    (with-timeout (2 (kill-process proc)) ;; on timeout, kill the process
      (while (process-live-p proc) ;; while process is running
        (sit-for .05)) ;; let emacs read events and run timmers (and check for timeout)
      (message "finished on time!!")))) ;; this will run only if there is no timeout
Jordon Biondo
quelle
Ich habe ein kleines Problem damit. Es wird nicht garantiert, dass sich die Ausgabe der Prozesse nach Beendigung der Schleife im Ausgabepuffer befindet. Ich arbeite daran, indem ich (sit-for 0.2)nach der whileSchleife einen anderen anrufe, aber das fühlt sich instabil an. Gibt es eine Möglichkeit zu wissen, dass die Ausgabe auf dem Puffer gedruckt wurde?
Malabarba
Da bin ich mir nicht sicher. Ich würde jedoch annehmen, dass der Vorgang ohnehin nicht ausgeführt wird, wenn Sie das Timeout erreichen, sodass die Ausgabe nicht relevant ist. Was machen Sie so, dass die Ausgabe auch nach einer Zeitüberschreitung noch benötigt wird? Sie könnten möglicherweise auf die Verwendung eines Prozessfilters umsteigen und den Prozess beenden. Nur X Sekunden sind ohne Ausgabe vergangen und nicht nach X Sekunden, egal was passiert.
Jordon Biondo
Entschuldigung, ich war nicht klar. Die Ausgabe ist mir egal, wenn das Timeout erreicht ist. Ich sage, die Ausgabe wird nicht sofort gedruckt, nachdem der Prozess erfolgreich abgeschlossen wurde .
Malabarba
Ich denke, das Problem ist (sit-for 0), dass emacs zu viel Zeit damit verbringt, den Prozess laufen zu lassen und nicht genug auf Ereignisse zu warten und den Text in den Puffer einzufügen. Wenn ich meine auf (sit-for .05)Dinge ändere, funktionieren die Dinge besser und der gesamte Text gelangt in meinen Puffer. Selbst für einen kurzen Prozess sind 0,05 Sekunden kein besorgniserregender Overhead.
Jordon Biondo
Ich mache mir in dieser Situation keine allzu großen Sorgen um den Overhead. Der Vorgang wird 5-8 Mal aufgerufen und das Ganze ist eine interaktive Operation, die bereits mindestens einige Sekunden dauert, sodass 1 weitere Sekunde keinen großen Unterschied macht. Was mich ein wenig beunruhigt, ist die Stabilität, da ich (sit-for 0.2)meine lokalen Tests zum Bestehen verwenden musste, und selbst dann, wenn ich die Änderungen vorgenommen habe, sind die Travis-Tests fehlgeschlagen.
Malabarba
4

Sie können dies erreichen, indem Sie Ihrem Shell-Befehl lediglich einen GNU-Timeout- Aufruf hinzufügen , der die Notwendigkeit umgeht, Details zum Emacs-Verhalten zu kennen. Zum Beispiel laufen:

$ timeout 5 sleep 10

Wird in 5 Sekunden zurückkehren, nicht in 10 (Timeout drückt effektiv Strg-C für Sie).

Joseph Garvin
quelle
Vielen Dank, ich muss mich möglicherweise für diese Option entscheiden, wenn ich mit der anderen keine Stabilität erreichen kann. Ich hätte es jedoch vorgezogen, mich nicht auf ein anderes Dienstprogramm zu verlassen, damit es auch unter Windows funktioniert.
Malabarba