aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2019-01-15 10:18:45 -0800
committerPaul Eggert2019-01-15 10:21:09 -0800
commit9fc02ff5ea95c31a8d81eabb5634aa135fcd8786 (patch)
treec6a8b2db65efc5b51a184658a0672f89b625015e
parent223e7b87872d4a010ae1c9a6f09a9c15aee46692 (diff)
downloademacs-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.texi20
-rw-r--r--lisp/net/tramp-adb.el6
-rw-r--r--lisp/net/tramp-rclone.el6
-rw-r--r--lisp/net/tramp-smb.el19
-rw-r--r--lisp/net/tramp-sudoedit.el2
-rw-r--r--lisp/net/tramp.el9
-rw-r--r--test/src/process-tests.el4
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
1859arrived. 1859arrived.
1860@end defun 1860@end defun
1861 1861
1862If a connection from a process contains buffered data,
1863@code{accept-process-output} can return non-@code{nil} even after the
1864process 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
1873will 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
1875contains 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))