Cleanups

Cleanups are a structured way to deal with things that need to be done later.

When your code does something (e.g., xmalloc some memory, or open a file) that needs to be undone later (e.g., xfree the memory or close the file), it can make a cleanup. The cleanup will be done at some future point: when the command is finished and control returns to the top level; when an error occurs and the stack is unwound; or when your code decides it’s time to explicitly perform cleanups. Alternatively you can elect to discard the cleanups you created.

Basics

struct cleanup *old_chain;

Declare a variable which will hold a cleanup chain handle.

old_chain = make_cleanup (function, arg);

Make a cleanup which will cause function to be called with arg (a void *) later. The result, old_chain, is a handle that can later be passed to do_cleanups or discard_cleanups. Unless you are going to call do_cleanups or discard_cleanups, you can ignore the result from make_cleanup.

do_cleanups (old_chain);

Do all cleanups added to the chain since the corresponding make_cleanup call was made.

discard_cleanups (old_chain);

Same as do_cleanups except that it just removes the cleanups from the chain and does not call the specified functions.

Cleanups are implemented as a chain. The handle returned by make_cleanups includes the cleanup passed to the call and any later cleanups appended to the chain (but not yet discarded or performed). E.g.:

make_cleanup (a, 0); 
{
  struct cleanup *old = make_cleanup (b, 0); 
  make_cleanup (c, 0)
  ...
  do_cleanups (old);
}

will call c() and b() but will not call a(). The cleanup that calls a() will remain in the cleanup chain, and will be done later unless otherwise discarded.

Recommendations

Your function should explicitly do or discard the cleanups it creates. Failing to do this leads to non-deterministic behavior since the caller will arbitrarily do or discard your functions cleanups. Failure to deal with local cleanups can also lead to crashes if there is a cleanup installed that refers to variables on the stack -- when the cleanup does finally run, it will refer to invalid memory.

There is a cleanup-checking GCC plugin in gdb/contrib/cleanup_check.py that can be used to check whether cleanups conform to the normal rules. Note, however, that there are some spots in GDB that do not follow the rules, so you must carefully interpret the plugin's output.

When working on gdb, it's best to assume that any function might throw an exception. (In specific cases, a function might document that it does not throw.) This means that resources should be guarded with a cleanup in most situations.

Some functions, e.g., fputs_filtered() or error(), specify that they “should not be called when cleanups are not in place”. This is an older way of saying that they might throw an exception; but of course nowadays the default should be to assume that a function may throw.

Typical styles

The first style is try/finally. Before it exits, your code-block calls do_cleanups with the old cleanup chain and thus ensures that your code-block's cleanups are always performed. For instance, the following code-segment avoids a memory leak problem (even when error is called and a forced stack unwind occurs) by ensuring that the xfree will always be called:

struct cleanup *old = make_cleanup (null_cleanup, 0);
data = xmalloc (sizeof blah);
make_cleanup (xfree, data);
... blah blah ...
do_cleanups (old);

The second style is try/except. Before it exits, your code-block calls discard_cleanups with the old cleanup chain and thus ensures that any created cleanups are not performed. For instance, the following code segment, ensures that the file will be closed but only if there is an error:

FILE *file = fopen ("afile", "r");
struct cleanup *old = make_cleanup (close_file, file);
... blah blah ...
discard_cleanups (old);
return file;

None: Internals Cleanups (last edited 2016-03-10 23:57:43 by TomTromey)

All content (C) 2008 Free Software Foundation. For terms of use, redistribution, and modification, please see the WikiLicense page.