Previous: , Up: Nonlocal Exits   [Contents][Index]


15.5.4 Cleaning Up from Nonlocal Exits

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

Special Form: unwind-protect body cleanup-forms…

unwind-protect executes the body with a guarantee that the cleanup-forms will be evaluated if control leaves body, no matter how that happens. The body 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 the body forms finish normally, unwind-protect returns the value of the last body form, after it evaluates the cleanup-forms. If the body forms do not finish, unwind-protect does not return any value in the normal sense.

Only the body is actually protected by the unwind-protect. If any of the cleanup-forms themselves exits nonlocally (e.g., 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.

The number of currently active unwind-protect forms counts, together with the number of local variable bindings, against the limit max-specpdl-size (see Local Variables).

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

(save-excursion
  (let ((buffer (get-buffer-create " *temp*")))
    (set-buffer buffer)
    (unwind-protect
        body
      (kill-buffer buffer))))

You might think that we could just as well write (kill-buffer (current-buffer)) and dispense with the variable buffer. However, the way shown above is safer, if body happens to get an error after switching to a different buffer! Alternatively, you could write another save-excursion around the body, to ensure that the temporary buffer becomes current in time to kill it.

Here is an actual example taken from the file ftp.el. It creates a process (see Processes) 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, SXEmacs 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 actually 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.

Here is another example which uses unwind-protect to make sure to kill a temporary buffer. In this example, the value returned by unwind-protect is used.

(defun shell-command-string (cmd)
  "Return the output of the shell command CMD, as a string."
  (save-excursion
    (set-buffer (generate-new-buffer " OS*cmd"))
    (shell-command cmd t)
    (unwind-protect
        (buffer-string)
      (kill-buffer (current-buffer)))))

Previous: , Up: Nonlocal Exits   [Contents][Index]