summaryrefslogtreecommitdiff
path: root/devdocs/elisp/cleanups.html
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2024-04-07 13:41:34 -0500
committerCraig Jennings <c@cjennings.net>2024-04-07 13:41:34 -0500
commit754bbf7a25a8dda49b5d08ef0d0443bbf5af0e36 (patch)
treef1190704f78f04a2b0b4c977d20fe96a828377f1 /devdocs/elisp/cleanups.html
new repository
Diffstat (limited to 'devdocs/elisp/cleanups.html')
-rw-r--r--devdocs/elisp/cleanups.html27
1 files changed, 27 insertions, 0 deletions
diff --git a/devdocs/elisp/cleanups.html b/devdocs/elisp/cleanups.html
new file mode 100644
index 00000000..90eb768e
--- /dev/null
+++ b/devdocs/elisp/cleanups.html
@@ -0,0 +1,27 @@
+ <h4 class="subsection">Cleaning Up from Nonlocal Exits</h4> <p>The <code>unwind-protect</code> 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. (Another more specific cleanup construct that is used only for changes in buffer contents is the atomic change group; <a href="atomic-changes">Atomic Changes</a>.) </p> <dl> <dt id="unwind-protect">Special Form: <strong>unwind-protect</strong> <em>body-form cleanup-forms…</em>
+</dt> <dd>
+ <p><code>unwind-protect</code> executes <var>body-form</var> with a guarantee that the <var>cleanup-forms</var> will be evaluated if control leaves <var>body-form</var>, no matter how that happens. <var>body-form</var> may complete normally, or execute a <code>throw</code> out of the <code>unwind-protect</code>, or cause an error; in all cases, the <var>cleanup-forms</var> will be evaluated. </p> <p>If <var>body-form</var> finishes normally, <code>unwind-protect</code> returns the value of <var>body-form</var>, after it evaluates the <var>cleanup-forms</var>. If <var>body-form</var> does not finish, <code>unwind-protect</code> does not return any value in the normal sense. </p> <p>Only <var>body-form</var> is protected by the <code>unwind-protect</code>. If any of the <var>cleanup-forms</var> themselves exits nonlocally (via a <code>throw</code> or an error), <code>unwind-protect</code> is <em>not</em> guaranteed to evaluate the rest of them. If the failure of one of the <var>cleanup-forms</var> has the potential to cause trouble, then protect it with another <code>unwind-protect</code> around that form. </p> <p>The number of currently active <code>unwind-protect</code> forms counts, together with the number of local variable bindings, against the limit <code>max-specpdl-size</code> (see <a href="local-variables#Definition-of-max_002dspecpdl_002dsize">Local Variables</a>). </p>
+</dd>
+</dl> <p>For example, here we make an invisible buffer for temporary use, and make sure to kill it before finishing: </p> <div class="example"> <pre class="example">(let ((buffer (get-buffer-create " *temp*")))
+ (with-current-buffer buffer
+ (unwind-protect
+ <var>body-form</var>
+ (kill-buffer buffer))))
+</pre>
+</div> <p>You might think that we could just as well write <code>(kill-buffer
+(current-buffer))</code> and dispense with the variable <code>buffer</code>. However, the way shown above is safer, if <var>body-form</var> happens to get an error after switching to a different buffer! (Alternatively, you could write a <code>save-current-buffer</code> around <var>body-form</var>, to ensure that the temporary buffer becomes current again in time to kill it.) </p> <p>Emacs includes a standard macro called <code>with-temp-buffer</code> which expands into more or less the code shown above (see <a href="current-buffer#Definition-of-with_002dtemp_002dbuffer">Current Buffer</a>). Several of the macros defined in this manual use <code>unwind-protect</code> in this way. </p> <p>Here is an actual example derived from an FTP package. It creates a process (see <a href="processes">Processes</a>) to try to establish a connection to a remote machine. As the function <code>ftp-login</code> 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. </p> <div class="example"> <pre class="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)))))
+</pre>
+</div> <p>This example has a small bug: if the user types <kbd>C-g</kbd> to quit, and the quit happens immediately after the function <code>ftp-setup-buffer</code> returns but before the variable <code>process</code> is set, the process will not be killed. There is no easy way to fix this bug, but at least it is very unlikely. </p>
+<hr><div class="_attribution">
+ <p class="_attribution-p">
+ Copyright &copy; 1990-1996, 1998-2022 Free Software Foundation, Inc. <br>Licensed under the GNU GPL license.<br>
+ <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Cleanups.html" class="_attribution-link">https://www.gnu.org/software/emacs/manual/html_node/elisp/Cleanups.html</a>
+ </p>
+</div>