0

Why does the following sexp hang?

(let ((buf
          "*Async Shell Command*"))
     (async-shell-command "cat")
     (while (get-buffer-process buf)
       (message "still has proc")
       (kill-process (get-buffer-process buf))))
  • You should use when, not while. – xuchunyang May 25 '17 at 5:43
  • I expect the loop condition will eventually evaluate to nil, why it does not is my question – erjoalgo May 25 '17 at 6:04
  • I don't think that kill-process will unset the buffer's variable. Without testing, I'd guess that it will simply change status of the process to "exited" or something like that. – wvxvw May 25 '17 at 7:19
2

Another attempt at explaining the situation:

(let ((buf "*Async Shell Command*"))
  (async-shell-command "cat")
  (while (let ((bp (get-buffer-process buf)))
           (message "status: %s" (process-status bp))
           bp)
    (message "still has proc")
    (kill-process (get-buffer-process buf))
    (accept-process-output)
    (message "Letting emacs to deal with closing a process")))

Then switch to the *Messages* buffer and look at the output, you should see:

status: run
still has proc
Letting emacs to deal with closing a process
status: signal
still has proc
cat: killed.
Letting emacs to deal with closing a process
Entering debugger...

What I believe is happening:

When Emacs starts a process, it sets its hooks for processing its events. A hook dealing with event related to killing the process needs to run in the same thread in which your while loop is executing, but, unless while yields (it may sleep for example), Emacs will not be able to execute the hook. So it will appear as if the process is still there. This is very similar to the typical problems in cooperative multitasking schemes, where processes must make room for other processes to run, or otherwise some processes will never get the possibility to execute.

  • +1. For anyone interested, compare kill-process, which merely signals the process, with delete-process, which immediately disconnects Emacs from the process. – Basil May 25 '17 at 13:53
0

kill-process is working in your case. But for a reason get-buffer-process does not return nil to while. Maybe because it refers to a buffer which is not yet closed, and emacs needs to take a breath? A sit-for command will fix this.

A solution would be:

(let ((buf
       "*Async Shell Command*"))
  (async-shell-command "cat")
  (while (get-buffer-process buf)
    (message "still has proc")
    (kill-process (get-buffer-process buf))
    (sit-for 0.01)))

Another solution could be not to use get-buffer-process:

(let ((buf
          "*Async Shell Command*"))
     (async-shell-command "cat")
     (while (not (string= (kill-process buf) buf))
       (message "still has proc")))
  • In my case get-buffer-process never returns nil within a loop that doesn't call sit-for. Try it yourself: (progn (start-process "p" "b" "cat") (dotimes (i 10) (message "> %s" (get-buffer-process "b")) (kill-process "p"))). – Basil May 25 '17 at 12:42
  • Basil, yes it looks like 'get-buffer-process' does not return nil with your code, even when counting to 1M. My second Solution is working completely without 'get-buffer-process' But the questioneer asked 'why' not for a solution. :) – jue May 25 '17 at 13:19
  • Sure, but it seems to me that in your answer both the "why" (get-buffer-process returns nil) and the "solution" (using sit-for) are wrong. What am I missing? – Basil May 25 '17 at 13:27
  • 1. Why is the solution to use 'sit-for' wrong? 2. The questioner asked why is the sexp hanging, so the reason is 'get-buffer-process is not returning nil', I edited my answer. – jue May 25 '17 at 13:40
  • The explanation of wvxvw looks reasonable. – jue May 25 '17 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.