diff options
| author | Jim Porter | 2024-07-06 14:09:08 -0700 |
|---|---|---|
| committer | Jim Porter | 2024-07-09 17:28:32 -0700 |
| commit | 8e46f44ea0eb761e24beda8c5cdbc8fcca87307a (patch) | |
| tree | 9b9056b88b716f82e926a2a3896695b9ea11b65d | |
| parent | 342998511add79c594a170dc04ecda2f2db0fd36 (diff) | |
| download | emacs-8e46f44ea0eb761e24beda8c5cdbc8fcca87307a.tar.gz emacs-8e46f44ea0eb761e24beda8c5cdbc8fcca87307a.zip | |
Improve Eshell's behavior when waiting for processes
This has a few benefits. First, it fixes a race condition when killing
old processes in 'eshell-command'. Second, the "wait" built-in command
is now more useful. Finally, killing processes when exiting Eshell (via
'eshell-round-robin-kill') should be much faster.
* lisp/eshell/esh-proc.el (esh-opt): Require.
(eshell-wait-for-process): Make obsolete in favor of...
(eshell-wait-for-processes): ... this. Accept a timeout and support
PIDs. Update callers.
(eshell/wait): New implementation accepting -t/--timeout.
(eshell-round-robin-kill): Use 'eshell-wait-for-processes'.
* lisp/eshell/eshell.el (eshell-command): Use 'eshell-round-robin-kill'.
* doc/misc/eshell.texi (List of Built-ins): Document the new "wait"
behavior.
* etc/NEWS: Announce this change.
| -rw-r--r-- | doc/misc/eshell.texi | 7 | ||||
| -rw-r--r-- | etc/NEWS | 6 | ||||
| -rw-r--r-- | lisp/eshell/esh-cmd.el | 2 | ||||
| -rw-r--r-- | lisp/eshell/esh-proc.el | 65 | ||||
| -rw-r--r-- | lisp/eshell/eshell.el | 7 |
5 files changed, 62 insertions, 25 deletions
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 69f94fab469..8547131194e 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi | |||
| @@ -1201,8 +1201,11 @@ or a string, referring to an environment variable. | |||
| 1201 | 1201 | ||
| 1202 | @cmindex wait | 1202 | @cmindex wait |
| 1203 | @cindex processes, waiting for | 1203 | @cindex processes, waiting for |
| 1204 | @item wait [@var{process}]@dots{} | 1204 | @item wait [-t @var{timeout}] [@var{process}]@dots{} |
| 1205 | Wait until each specified @var{process} has exited. | 1205 | Wait until each specified @var{process} has exited. Processes can |
| 1206 | either be process objects (@pxref{Processes, , , elisp, GNU Emacs Lisp | ||
| 1207 | Reference Manual}) or integer PIDs. If you pass @code{-t} or | ||
| 1208 | @code{--timeout}, wait at most that many seconds before exiting. | ||
| 1206 | 1209 | ||
| 1207 | @cmindex which | 1210 | @cmindex which |
| 1208 | @item which @var{command}@dots{} | 1211 | @item which @var{command}@dots{} |
| @@ -54,6 +54,12 @@ this will prompt for confirmation before creating a new buffer when | |||
| 54 | necessary. To restore the previous behavior, set this option to | 54 | necessary. To restore the previous behavior, set this option to |
| 55 | 'confirm-kill-process'. | 55 | 'confirm-kill-process'. |
| 56 | 56 | ||
| 57 | +++ | ||
| 58 | *** Eshell's built-in "wait" command now accepts a timeout. | ||
| 59 | By passing "-t" or "--timeout", you can specify a maximum time to wait | ||
| 60 | for the processes to exit. Additionally, you can now wait for external | ||
| 61 | processes by passing their PIDs. | ||
| 62 | |||
| 57 | ** SHR | 63 | ** SHR |
| 58 | 64 | ||
| 59 | +++ | 65 | +++ |
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 0b3137127d2..b936f68a57a 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el | |||
| @@ -1299,7 +1299,7 @@ have been replaced by constants." | |||
| 1299 | (if-let (((memq (car form) eshell-deferrable-commands)) | 1299 | (if-let (((memq (car form) eshell-deferrable-commands)) |
| 1300 | (procs (eshell-make-process-list result))) | 1300 | (procs (eshell-make-process-list result))) |
| 1301 | (if synchronous-p | 1301 | (if synchronous-p |
| 1302 | (apply #'eshell/wait procs) | 1302 | (funcall #'eshell-wait-for-processes procs) |
| 1303 | (eshell-manipulate form "inserting ignore form" | 1303 | (eshell-manipulate form "inserting ignore form" |
| 1304 | (setcar form 'ignore) | 1304 | (setcar form 'ignore) |
| 1305 | (setcdr form nil)) | 1305 | (setcdr form nil)) |
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el index f982e2101f5..0dcdf3bb76c 100644 --- a/lisp/eshell/esh-proc.el +++ b/lisp/eshell/esh-proc.el | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | 25 | ||
| 26 | (require 'esh-arg) | 26 | (require 'esh-arg) |
| 27 | (require 'esh-io) | 27 | (require 'esh-io) |
| 28 | (require 'esh-opt) | ||
| 28 | (require 'esh-util) | 29 | (require 'esh-util) |
| 29 | 30 | ||
| 30 | (require 'pcomplete) | 31 | (require 'pcomplete) |
| @@ -184,16 +185,46 @@ This is like `process-live-p', but additionally checks whether | |||
| 184 | ;; cleared out the handles (see `eshell-sentinel'). | 185 | ;; cleared out the handles (see `eshell-sentinel'). |
| 185 | (process-get process :eshell-handles))) | 186 | (process-get process :eshell-handles))) |
| 186 | 187 | ||
| 187 | (defun eshell-wait-for-process (&rest procs) | 188 | (defun eshell-wait-for-processes (&optional procs timeout) |
| 188 | "Wait until PROCS have successfully completed." | 189 | "Wait until PROCS have completed execution. |
| 189 | (dolist (proc procs) | 190 | If TIMEOUT is non-nil, wait at most that many seconds. Return non-nil |
| 190 | (when (eshell-processp proc) | 191 | if all the processes finished executing before the timeout expired." |
| 191 | (while (eshell-process-active-p proc) | 192 | (let ((expiration (when timeout (time-add (current-time) timeout)))) |
| 192 | (when (input-pending-p) | 193 | (catch 'timeout |
| 193 | (discard-input)) | 194 | (dolist (proc procs) |
| 194 | (sit-for eshell-process-wait-time))))) | 195 | (while (if (processp proc) |
| 196 | (eshell-process-active-p proc) | ||
| 197 | (process-attributes proc)) | ||
| 198 | (when (input-pending-p) | ||
| 199 | (discard-input)) | ||
| 200 | (when (and expiration | ||
| 201 | (not (time-less-p (current-time) expiration))) | ||
| 202 | (throw 'timeout nil)) | ||
| 203 | (sit-for eshell-process-wait-time))) | ||
| 204 | t))) | ||
| 195 | 205 | ||
| 196 | (defalias 'eshell/wait #'eshell-wait-for-process) | 206 | (defun eshell-wait-for-process (&rest procs) |
| 207 | "Wait until PROCS have completed execution." | ||
| 208 | (declare (obsolete 'eshell-wait-for-processes "31.1")) | ||
| 209 | (eshell-wait-for-processes procs)) | ||
| 210 | |||
| 211 | (defun eshell/wait (&rest args) | ||
| 212 | "Wait until processes have completed execution." | ||
| 213 | (eshell-eval-using-options | ||
| 214 | "wait" args | ||
| 215 | '((?h "help" nil nil "show this usage screen") | ||
| 216 | (?t "timeout" t timeout "timeout in seconds") | ||
| 217 | :preserve-args | ||
| 218 | :show-usage | ||
| 219 | :usage "[OPTION] PROCESS... | ||
| 220 | Wait until PROCESS(es) have completed execution.") | ||
| 221 | (when (stringp timeout) | ||
| 222 | (setq timeout (string-to-number timeout))) | ||
| 223 | (dolist (arg args) | ||
| 224 | (unless (or (processp arg) (natnump arg)) | ||
| 225 | (error "wait: invalid argument type: %s" (type-of arg)))) | ||
| 226 | (unless (eshell-wait-for-processes args timeout) | ||
| 227 | (error "wait: timed out after %s seconds" timeout)))) | ||
| 197 | 228 | ||
| 198 | (defun eshell/jobs () | 229 | (defun eshell/jobs () |
| 199 | "List processes, if there are any." | 230 | "List processes, if there are any." |
| @@ -626,16 +657,14 @@ long to delay between signals." | |||
| 626 | (defun eshell-round-robin-kill (&optional query) | 657 | (defun eshell-round-robin-kill (&optional query) |
| 627 | "Kill current process by trying various signals in sequence. | 658 | "Kill current process by trying various signals in sequence. |
| 628 | See the variable `eshell-kill-processes-on-exit'." | 659 | See the variable `eshell-kill-processes-on-exit'." |
| 629 | (let ((sigs eshell-kill-process-signals)) | 660 | (catch 'done |
| 630 | (while sigs | 661 | (dolist (sig eshell-kill-process-signals) |
| 631 | (eshell-process-interact | 662 | (eshell-process-interact |
| 632 | (lambda (proc) | 663 | (lambda (proc) (signal-process proc sig)) t query) |
| 633 | (signal-process (process-id proc) (car sigs))) t query) | 664 | (when (eshell-wait-for-processes (mapcar #'car eshell-process-list) |
| 634 | (setq query nil) | 665 | eshell-kill-process-wait-time) |
| 635 | (if (not eshell-process-list) | 666 | (throw 'done nil)) |
| 636 | (setq sigs nil) | 667 | (setq query nil)))) |
| 637 | (sleep-for eshell-kill-process-wait-time) | ||
| 638 | (setq sigs (cdr sigs)))))) | ||
| 639 | 668 | ||
| 640 | (defun eshell-query-kill-processes () | 669 | (defun eshell-query-kill-processes () |
| 641 | "Kill processes belonging to the current Eshell buffer, possibly with query." | 670 | "Kill processes belonging to the current Eshell buffer, possibly with query." |
diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el index 568f6745067..b7be3dd1643 100644 --- a/lisp/eshell/eshell.el +++ b/lisp/eshell/eshell.el | |||
| @@ -176,7 +176,7 @@ | |||
| 176 | (require 'cl-lib)) | 176 | (require 'cl-lib)) |
| 177 | (require 'esh-util) | 177 | (require 'esh-util) |
| 178 | (require 'esh-module) ;For eshell-using-module | 178 | (require 'esh-module) ;For eshell-using-module |
| 179 | (require 'esh-proc) ;For eshell-wait-for-process | 179 | (require 'esh-proc) ;For eshell-wait-for-processes |
| 180 | (require 'esh-io) ;For eshell-last-command-status | 180 | (require 'esh-io) ;For eshell-last-command-status |
| 181 | (require 'esh-cmd) | 181 | (require 'esh-cmd) |
| 182 | 182 | ||
| @@ -357,8 +357,7 @@ buffer is already taken by another running shell command." | |||
| 357 | (with-current-buffer bufname | 357 | (with-current-buffer bufname |
| 358 | ;; Stop all the processes in the old buffer (there may | 358 | ;; Stop all the processes in the old buffer (there may |
| 359 | ;; be several). | 359 | ;; be several). |
| 360 | (eshell-process-interact #'interrupt-process t)) | 360 | (eshell-round-robin-kill)) |
| 361 | (accept-process-output) | ||
| 362 | (kill-buffer bufname)) | 361 | (kill-buffer bufname)) |
| 363 | ((eq eshell-command-async-buffer 'confirm-new-buffer) | 362 | ((eq eshell-command-async-buffer 'confirm-new-buffer) |
| 364 | (shell-command--same-buffer-confirm "Use a new buffer") | 363 | (shell-command--same-buffer-confirm "Use a new buffer") |
| @@ -377,7 +376,7 @@ buffer is already taken by another running shell command." | |||
| 377 | ;; make the output as attractive as possible, with no | 376 | ;; make the output as attractive as possible, with no |
| 378 | ;; extraneous newlines | 377 | ;; extraneous newlines |
| 379 | (unless async | 378 | (unless async |
| 380 | (apply #'eshell-wait-for-process (cadr eshell-foreground-command)) | 379 | (funcall #'eshell-wait-for-processes (cadr eshell-foreground-command)) |
| 381 | (cl-assert (not eshell-foreground-command)) | 380 | (cl-assert (not eshell-foreground-command)) |
| 382 | (goto-char (point-max)) | 381 | (goto-char (point-max)) |
| 383 | (while (and (bolp) (not (bobp))) | 382 | (while (and (bolp) (not (bobp))) |