help-gnu-emacs
[Top][All Lists]
Advanced

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

unwind-protect and inhibit-quit


From: Felix Dietrich
Subject: unwind-protect and inhibit-quit
Date: Thu, 15 Jul 2021 17:14:56 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux)

Hello,

I have questions regarding “unwind-protect” and the two examples
provided in the elisp reference manual [1].


    ;; 1. Example
    (let ((buffer (get-buffer-create " *temp*")))
      (with-current-buffer buffer
        (unwind-protect
            body-form
          (kill-buffer buffer))))


    ;; 2. Example
    (let ((win nil))
      (unwind-protect
          (progn
            (setq process (ftp-setup-buffer host file))
            (if (setq win (ftp-login process host user password))
                (message "Logged in")
              (error "Ftp login failed")))
        (or win (and process (delete-process process)))))


In the last paragraph [2], the description of the second example mentions a
race condition between the assignment of the “ftp-setup-buffer”
functionʼs return value to the variable “process” and the user sending a
quit signal (C-g).  Does the first example suffer from the same issue in
itʼs assignment to the “buffer” variable?

That paragraph also mentions that “there is no easy way to fix this
bug”.  Is that statement still true or can this issue be resolved by
setting “inhibit-quit” in current Emacs versions:


    ;; 2. Example (added inhibit-quit)
    (let ((win nil))
      (unwind-protect
          (progn
            (let ((inhibit-quit t))
              (setq process (ftp-setup-buffer host file)))
            (if (setq win (ftp-login process host user password))
                (message "Logged in")
              (error "Ftp login failed")))
        (or win (and process (delete-process process)))))


The first example has the “unwind-protect” form nested inside
“with-current-buffer”; this leaves a window where the buffer is both
created and assigned to the variable “buffer” but not protected from
being left alive in case of a quit (or, in more elaborated uses, an
error).  Shouldn’t it, therefore, be the other way around:
“unwind-protect” wrapping “with-current-buffer”?  In the following
snippet I also have added “inhibit-quit”, assuming that it works like I
expect.


    ;; 1. Example (moved unwind-protect up; added inhibit-quit)
    (let (buffer)
      (unwind-protect
          (progn
            (let ((inhibit-quit t))
              (setq buffer (get-buffer-create " *temp*")))
            (with-current-buffer buffer
              body-form))
        (and buffer (kill-buffer buffer))))


There also seems to be an issue in the first example with modifying and
killing a potentially already existing buffer " *temp*", which could be
avoided, I believe, by using “generate-new-buffer”:


    ;; 1. Example (moved unwind-protect up; added inhibit-quit;
    ;;             use generate-new-buffer)
    (let (buffer)
      (unwind-protect
          (progn
            (let ((inhibit-quit t))
              (setq buffer (generate-new-buffer " *temp*")))
            (with-current-buffer buffer
              body-form))
        (and buffer (kill-buffer buffer))))


Does “generate-new-buffer” make the same guarantee as
“get-buffer-create” that it will never return nil?  At least
“generate-new-buffer” is implemented with “get-buffer-create”.


Footnotes:
[1]  <https://www.gnu.org/software/emacs/manual/html_node/elisp/Cleanups.html>
     
     (info "(elisp) Cleanups")

[2]  “This example has a small bug: if the user types C-g to quit, and the
     quit happens immediately after the function ftp-setup-buffer
     returns but before the variable process is set, the process will
     not be killed. There is no easy way to fix this bug, but at least
     it is very unlikely.”

-- 
Felix Dietrich



reply via email to

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