emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PROPOSAL: Control over process cleanup in `save-buffers-kill-emacs'.


From: Karl Fogel
Subject: PROPOSAL: Control over process cleanup in `save-buffers-kill-emacs'.
Date: Fri, 22 Sep 2017 13:58:41 -0500

In `save-buffers-kill-emacs' in files.el, we check for active processes -- the 
familiar "Active processes exist; kill them and exit anyway? " query -- and 
then only at the very end of the function do we run 
`kill-emacs-query-functions' and call `kill-emacs' (which then runs the hooks 
in `kill-emacs-hook' of course).

This means that neither `kill-emacs-query-functions' nor `kill-emacs-hook' 
happen in time to offer any fine-grained control over whether the user gets 
queried about active processes.  And since the `confirm-kill-processes' flag 
applies to all processes, one can't use it to customize *which* processes one 
is queried about at exit time.

I propose moving `kill-emacs-query-functions' to before the process-killing 
block, so that hooks can do things with processes before Emacs asks about those 
processes.  I haven't tested this patch yet, but this gives the idea:

  --- lisp/files.el
  +++ lisp/files.el
  @@ -6875,6 +6875,7 @@ save-buffers-kill-emacs
                                 (buffer-list))))
            (progn (setq confirm nil)
                   (yes-or-no-p "Modified buffers exist; exit anyway? ")))
  +     (run-hook-with-args-until-failure 'kill-emacs-query-functions)
        (or (not (fboundp 'process-list))
            ;; process-list is not defined on MSDOS.
            (not confirm-kill-processes)
  @@ -6897,8 +6898,6 @@ save-buffers-kill-emacs
                           (when (window-live-p window)
                             (quit-restore-window window 'kill)))))
                   (list-processes t)))))
  -     ;; Query the user for other things, perhaps.
  -     (run-hook-with-args-until-failure 'kill-emacs-query-functions)
        (or (null confirm)
            (funcall confirm "Really exit Emacs? "))
        (kill-emacs))))

Justification:

Sometimes there are expected processes that one knows it's okay to kill at exit 
time, but one would prefer to be queried about any unexpected processes 
(perhaps there is a long-running job that one forgot about, and one would 
answer "no" to exiting Emacs if one were reminded about that job).

For example, I just started using IMAP (instead of local spool source) to fetch 
email into Gnus.  Since I'm querying two separate IMAP servers, there are two 
separate IMAP buffers each with its own process.  When I exit Emacs, I do not 
need to be queried about those process buffers -- I know it's okay for Emacs to 
kill the processes.  But I can't easily do `set-process-query-on-exit-flag' at 
the time the IMAP processes are started, since I just run `M-x gnus' and then 
somewhen after that something runs IMAP (maybe there's a hook for this 
somewhere, but if so, I haven't found it yet).

But I could put code like this into a hook, if only there were a hook that were 
run before the process-killing block in `save-buffers-kill-emacs':

  (mapcar (lambda (buf)
            (when (string-match "^ \\*imap source\\*.*" (buffer-name buf))
              (message "shutting down IMAP process '%s'" (get-buffer-process 
buf)) 
              (set-process-query-on-exit-flag (get-buffer-process buf) nil)
              (kill-buffer buf)))
          (buffer-list))

I think it would be generally useful to offer the ability to have custom 
process cleanup at exit time.

My only reservation about the change is that the hook name 
`kill-emacs-query-functions' perhaps becomes even less accurate (though it is 
already potentially inaccurate, depending on how people are using it).  In 
fact, my personal use would be in order to *avoid* some queries, not to perform 
any new queries.  But for all I know, people are already using that hook to do 
things that have nothing to do with queries, so maybe moving it earlier doesn't 
add to the problem.

(A further possibility is to put the hook call at the very beginning of the 
function, even before the big `let', so that the hooks is run even before the 
"Modified buffers exist; exit anyway? " check.  I'm not sure whether that would 
be useful or not; since I don't have a concrete use case for it, I'm not 
pushing for it here.)

For reference, here's the entire function as it stands right now, current tip 
(commit 908af46abdb) of the "emacs-26" branch:

(defun save-buffers-kill-emacs (&optional arg)
  "Offer to save each buffer, then kill this Emacs process.
With prefix ARG, silently save all file-visiting buffers without asking.
If there are active processes where `process-query-on-exit-flag'
returns non-nil and `confirm-kill-processes' is non-nil,
asks whether processes should be killed.
Runs the members of `kill-emacs-query-functions' in turn and stops
if any returns nil.  If `confirm-kill-emacs' is non-nil, calls it."
  (interactive "P")
  ;; Don't use save-some-buffers-default-predicate, because we want
  ;; to ask about all the buffers before killing Emacs.
  (save-some-buffers arg t)
  (let ((confirm confirm-kill-emacs))
    (and
     (or (not (memq t (mapcar (function
                               (lambda (buf) (and (buffer-file-name buf)
                                                  (buffer-modified-p buf))))
                              (buffer-list))))
         (progn (setq confirm nil)
                (yes-or-no-p "Modified buffers exist; exit anyway? ")))
     (or (not (fboundp 'process-list))
         ;; process-list is not defined on MSDOS.
         (not confirm-kill-processes)
         (let ((processes (process-list))
               active)
           (while processes
             (and (memq (process-status (car processes)) '(run stop open 
listen))
                  (process-query-on-exit-flag (car processes))
                  (setq active t))
             (setq processes (cdr processes)))
           (or (not active)
               (with-current-buffer-window
                (get-buffer-create "*Process List*") nil
                #'(lambda (window _value)
                    (with-selected-window window
                      (unwind-protect
                          (progn
                            (setq confirm nil)
                            (yes-or-no-p "Active processes exist; kill them and 
exit anyway? "))
                        (when (window-live-p window)
                          (quit-restore-window window 'kill)))))
                (list-processes t)))))
     ;; Query the user for other things, perhaps.
     (run-hook-with-args-until-failure 'kill-emacs-query-functions)
     (or (null confirm)
         (funcall confirm "Really exit Emacs? "))
     (kill-emacs))))

Thoughts,
-Karl



reply via email to

[Prev in Thread] Current Thread [Next in Thread]