Re: [OB] Add cleanup, source.c

Jim Blandy
2007-07-03T18:13:34Z

What's going on here is that GDB uses one construct, cleanups, to
implement two different useful behaviors, and does so in a perilous
way.

In Java, you can write:

try {
...;
} catch {
...;
}

Or:

try {
...;
} finally {
...;
}

The 'catch' block gets executed if the 'try' block throws an
exception. The 'finally' block gets executed no matter whether the
try block terminates normally or throws an exception.

In GDB, when we push cleanups that we intend to discard on success,
those are playing the role of a 'catch' block. When we push cleanups
that we're counting on being called even if things go wrong, then
they're playing the role of a 'finally' block. They're both useful
features, and they're definitely not the same feature, because
'finally' blocks get called in more cases.

The problem with GDB is that we decide how to treat the blocks when we
clean up, not when we register. So here's the case we've been talking
about:

foo ()
{
make_cleanup (a 'finally' action)
do stuff which may throw an error
don't call do_cleanups, on at least one path out of the function
}

bar ()
{
make_cleanup (a 'catch' action)
foo ()
discard_cleanups, since we've succeeded and we don't want our catch
action to run
}

Here, if no errors occur, foo's finally action won't get run because
bar's discard_cleanups call applies more broadly than the author of
'bar' intended.

What GDB should do instead is this:

foo ()
{
finally_handler (a 'finally' action)
do stuff which may throw an error
don't call do_cleanups, at least one one path out
}

bar ()
{
failure_handler (a 'failure' action)
foo ()
cleanup_on_success ()
}

where 'cleanup_success' discards failure handlers and runs finally
handlers. The 'error' function would call a companion to that named
'cleanup_on_failure', which runs both finally and failure handlers.

So. Only 270 calls to 'do_cleanups' and 47 calls to
'discard_cleanups' to examine.