Previous: , Up: Nonlocal Exits   [Index]


F.6.3 Cleaning Up from Nonlocal Exits

Error Clean Up Concepts

The unwind-protect construct is essential whenever you temporarily put a data structure in an inconsistent state; it permits you to make the data consistent again in the event of an error or throw.

Function: unwind-protect body-form cleanup-forms…

unwind-protect executes ‘BODY-FORM’ with a guarantee that the ‘CLEANUP-FORMS’ will be evaluated if control leaves ‘BODY-FORM’, no matter how that happens. ‘BODY-FORM’ may complete normally, or execute a throw out of the unwind-protect, or cause an ‘error’; in all cases, the ‘CLEANUP-FORMS’ will be evaluated.

If ‘BODY-FORM’ finishes normally, unwind-protect returns the value of ‘BODY-FORM’, after it evaluates the ‘CLEANUP-FORMS’.

If ‘BODY-FORM’ does not finish, unwind-protect does not return any value in the normal sense.

Only ‘BODY-FORM’ is protected by the unwind-protect. If any of the ‘CLEANUP-FORMS’ themselves exits nonlocally (via a throw or an ‘error’), unwind-protect is not guaranteed to evaluate the rest of them. If the failure of one of the ‘CLEANUP-FORMS’ has the potential to cause trouble, then protect it with another unwind-protect around that form.

Macro ~with-temp-buffer~

For example, here we make an invisible buffer for temporary use, and make sure to kill it before finishing:

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

Emacs includes a standard macro called with-temp-buffer which expands into more or less the code shown above.

Example of ~unwind-protect~

Here is an actual example derived from an FTP package. It creates a ‘process’ to try to establish a connection to a remote machine. As the function ftp-login is highly susceptible to numerous problems that the writer of the function cannot anticipate, it is protected with a form that guarantees deletion of the process in the event of failure. Otherwise, Emacs might fill up with useless subprocesses.

(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)))))

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.


Previous: Errors—Unintentional Nonlocal Exits, Up: Nonlocal Exits   [Index]