diff options
| author | Paul Eggert | 2019-01-15 10:18:45 -0800 |
|---|---|---|
| committer | Paul Eggert | 2019-01-15 10:21:09 -0800 |
| commit | 9fc02ff5ea95c31a8d81eabb5634aa135fcd8786 (patch) | |
| tree | c6a8b2db65efc5b51a184658a0672f89b625015e | |
| parent | 223e7b87872d4a010ae1c9a6f09a9c15aee46692 (diff) | |
| download | emacs-9fc02ff5ea95c31a8d81eabb5634aa135fcd8786.tar.gz emacs-9fc02ff5ea95c31a8d81eabb5634aa135fcd8786.zip | |
Fix accept-process-output/process-live-p confusion
* doc/lispref/processes.texi (Accepting Output):
Document the issue.
* lisp/net/tramp-adb.el (tramp-adb-parse-device-names):
* lisp/net/tramp-rclone.el (tramp-rclone-parse-device-names):
* lisp/net/tramp-smb.el (tramp-smb-wait-for-output):
* lisp/net/tramp.el (tramp-interrupt-process):
* test/src/process-tests.el (make-process/mix-stderr):
Fix code that uses accept-process-output and process-live-p.
Add FIXME comments as necessary.
* lisp/net/tramp-sudoedit.el (tramp-sudoedit-action-sudo):
* lisp/net/tramp.el (tramp-action-out-of-band):
Add FIXME comments as necessary.
| -rw-r--r-- | doc/lispref/processes.texi | 20 | ||||
| -rw-r--r-- | lisp/net/tramp-adb.el | 6 | ||||
| -rw-r--r-- | lisp/net/tramp-rclone.el | 6 | ||||
| -rw-r--r-- | lisp/net/tramp-smb.el | 19 | ||||
| -rw-r--r-- | lisp/net/tramp-sudoedit.el | 2 | ||||
| -rw-r--r-- | lisp/net/tramp.el | 9 | ||||
| -rw-r--r-- | test/src/process-tests.el | 4 |
7 files changed, 47 insertions, 19 deletions
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi index 72b164c5d45..afda8aede83 100644 --- a/doc/lispref/processes.texi +++ b/doc/lispref/processes.texi | |||
| @@ -1859,6 +1859,26 @@ corresponding connection contains buffered data. The function returns | |||
| 1859 | arrived. | 1859 | arrived. |
| 1860 | @end defun | 1860 | @end defun |
| 1861 | 1861 | ||
| 1862 | If a connection from a process contains buffered data, | ||
| 1863 | @code{accept-process-output} can return non-@code{nil} even after the | ||
| 1864 | process has exited. Therefore, although the following loop: | ||
| 1865 | |||
| 1866 | @example | ||
| 1867 | ;; This loop contains a bug. | ||
| 1868 | (while (process-live-p process) | ||
| 1869 | (accept-process-output process)) | ||
| 1870 | @end example | ||
| 1871 | |||
| 1872 | @noindent | ||
| 1873 | will often work, it has a race condition and can miss some output if | ||
| 1874 | @code{process-live-p} returns @code{nil} while the connection still | ||
| 1875 | contains data. Better is to write the loop like this: | ||
| 1876 | |||
| 1877 | @example | ||
| 1878 | (while (or (accept-process-output process) | ||
| 1879 | (process-live-p process))) | ||
| 1880 | @end example | ||
| 1881 | |||
| 1862 | @node Processes and Threads | 1882 | @node Processes and Threads |
| 1863 | @subsection Processes and Threads | 1883 | @subsection Processes and Threads |
| 1864 | @cindex processes, threads | 1884 | @cindex processes, threads |
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index e2275bee2a4..ca47601e4bd 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el | |||
| @@ -206,9 +206,9 @@ pass to the OPERATION." | |||
| 206 | (tramp-message v 6 "%s" (mapconcat 'identity (process-command p) " ")) | 206 | (tramp-message v 6 "%s" (mapconcat 'identity (process-command p) " ")) |
| 207 | (process-put p 'adjust-window-size-function 'ignore) | 207 | (process-put p 'adjust-window-size-function 'ignore) |
| 208 | (set-process-query-on-exit-flag p nil) | 208 | (set-process-query-on-exit-flag p nil) |
| 209 | (while (process-live-p p) | 209 | ;; FIXME: Either remove " 0.1", or comment why it's needed. |
| 210 | (accept-process-output p 0.1)) | 210 | (while (or (accept-process-output p 0.1) |
| 211 | (accept-process-output p 0.1) | 211 | (process-live-p p))) |
| 212 | (tramp-message v 6 "\n%s" (buffer-string)) | 212 | (tramp-message v 6 "\n%s" (buffer-string)) |
| 213 | (goto-char (point-min)) | 213 | (goto-char (point-min)) |
| 214 | (while (search-forward-regexp "^\\(\\S-+\\)[[:space:]]+device$" nil t) | 214 | (while (search-forward-regexp "^\\(\\S-+\\)[[:space:]]+device$" nil t) |
diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el index 0aa110d9a46..73660572966 100644 --- a/lisp/net/tramp-rclone.el +++ b/lisp/net/tramp-rclone.el | |||
| @@ -183,9 +183,9 @@ pass to the OPERATION." | |||
| 183 | (tramp-message v 6 "%s" (mapconcat 'identity (process-command p) " ")) | 183 | (tramp-message v 6 "%s" (mapconcat 'identity (process-command p) " ")) |
| 184 | (process-put p 'adjust-window-size-function 'ignore) | 184 | (process-put p 'adjust-window-size-function 'ignore) |
| 185 | (set-process-query-on-exit-flag p nil) | 185 | (set-process-query-on-exit-flag p nil) |
| 186 | (while (process-live-p p) | 186 | ;; FIXME: Either remove " 0.1", or comment why it's needed. |
| 187 | (accept-process-output p 0.1)) | 187 | (while (or (accept-process-output p 0.1) |
| 188 | (accept-process-output p 0.1) | 188 | (process-live-p p))) |
| 189 | (tramp-message v 6 "\n%s" (buffer-string)) | 189 | (tramp-message v 6 "\n%s" (buffer-string)) |
| 190 | (goto-char (point-min)) | 190 | (goto-char (point-min)) |
| 191 | (while (search-forward-regexp "^\\(\\S-+\\):$" nil t) | 191 | (while (search-forward-regexp "^\\(\\S-+\\):$" nil t) |
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index 0c42a5d1a53..abf3248a353 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el | |||
| @@ -2038,10 +2038,10 @@ Returns nil if an error message has appeared." | |||
| 2038 | ;; Algorithm: get waiting output. See if last line contains | 2038 | ;; Algorithm: get waiting output. See if last line contains |
| 2039 | ;; `tramp-smb-prompt' sentinel or `tramp-smb-errors' strings. | 2039 | ;; `tramp-smb-prompt' sentinel or `tramp-smb-errors' strings. |
| 2040 | ;; If not, wait a bit and again get waiting output. | 2040 | ;; If not, wait a bit and again get waiting output. |
| 2041 | (while (and (not found) (not err) (process-live-p p)) | 2041 | ;; FIXME: Either remove " 0.1", or comment why it's needed. |
| 2042 | 2042 | (while (and (not found) (not err) | |
| 2043 | ;; Accept pending output. | 2043 | (or (tramp-accept-process-output p 0.1) |
| 2044 | (tramp-accept-process-output p 0.1) | 2044 | (process-live-p p))) |
| 2045 | 2045 | ||
| 2046 | ;; Search for prompt. | 2046 | ;; Search for prompt. |
| 2047 | (goto-char (point-min)) | 2047 | (goto-char (point-min)) |
| @@ -2052,10 +2052,13 @@ Returns nil if an error message has appeared." | |||
| 2052 | (setq err (re-search-forward tramp-smb-errors nil t))) | 2052 | (setq err (re-search-forward tramp-smb-errors nil t))) |
| 2053 | 2053 | ||
| 2054 | ;; When the process is still alive, read pending output. | 2054 | ;; When the process is still alive, read pending output. |
| 2055 | (while (and (not found) (process-live-p p)) | 2055 | ;; FIXME: This loop should be folded into the previous loop. |
| 2056 | 2056 | ;; Also, ERR should be set just once, after the combined | |
| 2057 | ;; Accept pending output. | 2057 | ;; loop has finished. |
| 2058 | (tramp-accept-process-output p 0.1) | 2058 | ;; FIXME: Either remove " 0.1", or comment why it's needed. |
| 2059 | (while (and (not found) | ||
| 2060 | (or (tramp-accept-process-output p 0.1) | ||
| 2061 | (process-live-p p))) | ||
| 2059 | 2062 | ||
| 2060 | ;; Search for prompt. | 2063 | ;; Search for prompt. |
| 2061 | (goto-char (point-min)) | 2064 | (goto-char (point-min)) |
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index cab9b8d835a..e1e5ab091a1 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el | |||
| @@ -746,6 +746,8 @@ ID-FORMAT valid values are `string' and `integer'." | |||
| 746 | (defun tramp-sudoedit-action-sudo (proc vec) | 746 | (defun tramp-sudoedit-action-sudo (proc vec) |
| 747 | "Check, whether a sudo process copy has finished." | 747 | "Check, whether a sudo process copy has finished." |
| 748 | ;; There might be pending output for the exit status. | 748 | ;; There might be pending output for the exit status. |
| 749 | ;; FIXME: Either remove " 0.1", or comment why it's needed. | ||
| 750 | ;; FIXME: There's a race here. Shouldn't the next two lines be interchanged? | ||
| 749 | (tramp-accept-process-output proc 0.1) | 751 | (tramp-accept-process-output proc 0.1) |
| 750 | (when (not (process-live-p proc)) | 752 | (when (not (process-live-p proc)) |
| 751 | ;; Delete narrowed region, it would be in the way reading a Lisp form. | 753 | ;; Delete narrowed region, it would be in the way reading a Lisp form. |
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 437e2d19b9e..7632d656a0f 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el | |||
| @@ -3977,6 +3977,8 @@ The terminal type can be configured with `tramp-terminal-type'." | |||
| 3977 | (defun tramp-action-out-of-band (proc vec) | 3977 | (defun tramp-action-out-of-band (proc vec) |
| 3978 | "Check, whether an out-of-band copy has finished." | 3978 | "Check, whether an out-of-band copy has finished." |
| 3979 | ;; There might be pending output for the exit status. | 3979 | ;; There might be pending output for the exit status. |
| 3980 | ;; FIXME: Either remove " 0.1", or comment why it's needed. | ||
| 3981 | ;; FIXME: Shouldn't the following line be wrapped inside (while ...)? | ||
| 3980 | (tramp-accept-process-output proc 0.1) | 3982 | (tramp-accept-process-output proc 0.1) |
| 3981 | (cond ((and (not (process-live-p proc)) | 3983 | (cond ((and (not (process-live-p proc)) |
| 3982 | (zerop (process-exit-status proc))) | 3984 | (zerop (process-exit-status proc))) |
| @@ -4821,9 +4823,10 @@ Only works for Bourne-like shells." | |||
| 4821 | ;; Wait, until the process has disappeared. If it doesn't, | 4823 | ;; Wait, until the process has disappeared. If it doesn't, |
| 4822 | ;; fall back to the default implementation. | 4824 | ;; fall back to the default implementation. |
| 4823 | (with-timeout (1 (ignore)) | 4825 | (with-timeout (1 (ignore)) |
| 4824 | (while (process-live-p proc) | 4826 | ;; We cannot run `tramp-accept-process-output', it blocks timers. |
| 4825 | ;; We cannot run `tramp-accept-process-output', it blocks timers. | 4827 | ;; FIXME: Either remove " 0.1", or comment why it's needed. |
| 4826 | (accept-process-output proc 0.1)) | 4828 | (while (or (accept-process-output proc 0.1) |
| 4829 | (process-live-p proc))) | ||
| 4827 | ;; Report success. | 4830 | ;; Report success. |
| 4828 | proc))))) | 4831 | proc))))) |
| 4829 | 4832 | ||
diff --git a/test/src/process-tests.el b/test/src/process-tests.el index 514bd04da4e..5dbf441e8c2 100644 --- a/test/src/process-tests.el +++ b/test/src/process-tests.el | |||
| @@ -207,8 +207,8 @@ | |||
| 207 | :sentinel #'ignore | 207 | :sentinel #'ignore |
| 208 | :noquery t | 208 | :noquery t |
| 209 | :connection-type 'pipe))) | 209 | :connection-type 'pipe))) |
| 210 | (while (process-live-p process) | 210 | (while (or (accept-process-output process) |
| 211 | (accept-process-output process)) | 211 | (process-live-p process))) |
| 212 | (should (eq (process-status process) 'exit)) | 212 | (should (eq (process-status process) 'exit)) |
| 213 | (should (eq (process-exit-status process) 0)) | 213 | (should (eq (process-exit-status process) 0)) |
| 214 | (should (process-tests--mixable (string-to-list (buffer-string)) | 214 | (should (process-tests--mixable (string-to-list (buffer-string)) |