User Tools

Special Operator UNWIND-PROTECT


  • unwind-protect protected-form cleanup-form*result*

Arguments and Values


unwind-protect evaluates protected-form and guarantees that cleanup-forms are executed before unwind-protect exits, whether it terminates normally or is aborted by a control transfer of some kind. unwind-protect is intended to be used to make sure that certain side effects take place after the evaluation of protected-form.

If a non-local exit occurs during execution of cleanup-forms, no special action is taken. The cleanup-forms of unwind-protect are not protected by that unwind-protect.

unwind-protect protects against all attempts to exit from protected-form, including go, handler-case, ignore-errors, restart-case, return-from, throw, and with-simple-restart.

Undoing of handler and restart bindings during an exit happens in parallel with the undoing of the bindings of dynamic variables and catch tags, in the reverse order in which they were established. The effect of this is that cleanup-form sees the same handler and restart bindings, as well as dynamic variable bindings and catch tags, as were visible when the unwind-protect was entered.


In the below example, when go is executed, the call to print is executed first, and then the transfer of control to the tag out is completed.

(tagbody (let ((x 3)) (unwind-protect (when (numberp x) (go out)) (print x))) out ...)

(defvar *state*)


(defun dummy-function (x) (setf *state* 'running) (unless (numberp x) (throw 'abort 'not-a-number)) (setf *state* (1+ x)))


(catch 'abort (dummy-function 1))




(catch 'abort (dummy-function 'trash))




(catch 'abort (unwind-protect (dummy-function 'trash) (setf *state* 'aborted)))




The following code is not correct:

(unwind-protect (incf *access-count*) (perform-access)) (decf *access-count*))

If an exit occurs before completion of incf, the decf form is executed anyway, resulting in an incorrect value for *access-count*. The correct way to code this is as follows:

(let ((old-count *access-count*)) (unwind-protect (progn (incf *access-count*) (perform-access)) (setf *access-count* old-count)))

(block nil (unwind-protect (return 1) (return 2)))


(block a (block b (unwind-protect (return-from a 1) (return-from b 2))))


This has undefined consequences.

(catch nil (unwind-protect (throw nil 1) (throw nil 2)))


The following has undefined consequences because the catch of b is passed over by the first throw, hence portable programs must assume its dynamic extent is terminated. The binding of the catch tag is not yet disestablished and therefore it is the target of the second throw.

(catch 'a (catch 'b (unwind-protect (throw 'a 1) (throw 'b 2))))

(catch 'foo (format t "The inner catch returns ~s.~%" (catch 'foo (unwind-protect (throw 'foo :first-throw) (throw 'foo :second-throw)))) :outer-catch)

The inner catch returns :SECOND-THROW


In the following example, the inner catch of a is passed over, but because that catch is disestablished before the throw to a is executed, it isn't seen.

(catch 'a (catch 'b (unwind-protect (1+ (catch 'a (throw 'b 1))) (throw 'a 10))))


The following has undefined consequences because the extent of the (catch 'bar ...) exit ends when the (throw 'foo ...) commences.

(catch 'foo (catch 'bar (unwind-protect (throw 'foo 3) (throw 'bar 4) (print 'xxx))))


This has undefined consequences.

In the following example, the (throw 'foo ...) has no effect on the scope of the bar catch tag or the extent of the (catch 'bar ...) exit.

(catch 'bar (catch 'foo (unwind-protect (throw 'foo 3) (throw 'bar 4) (print 'xxx))))


(block nil (let ((x 5)) (declare (special x)) (unwind-protect (return) (print x))))



Affected By


Exceptional Situations


See Also