“Is it a good idea for destructors to throw?”
— Common C++ interview question
A quick bit of code can show us.
#include <iostream> #include <stdexcept> namespace { class Foo { public: void someUsage() { std::cout << "Using foo" << std::endl; } ~Foo() { std::cout << "Destroying foo" << std::endl; throw std::runtime_error("Exception 2"); } }; } int main() { std::cout << "Program started" << std::endl; try { Foo f; f.someUsage(); throw std::runtime_error("Exception 1"); } catch (std::exception const & e) { std::cout << "Caught exception!" << std::endl; } return 0; }
Consider the above case where an object with a throwing destructor is popped from the stack. We naïvely hope our catch block will save us, but no, an exception has already thrown from another frame. We now have two exceptions to deal with and our program calls std::terminate
(thanks to Alan for a correction here – I originally claimed this was UB).
Program started Using foo Destroying foo terminate called after throwing an instance of 'std::runtime_error' what(): Exception 2 Aborted (core dumped)
This StackOverflow thread provides an excellent discussion of this issue from a C++ perspective. Many of the answers and comments gel nicely with the remaining content of this post, so it is well worth a read (but only once you’re done here, obviously).
What of our safe, garbage collected languages, though? What approach do we take to closing resources not managed by the VM?
If at first you don’t succeed…
…try
, catch
, finally
. This is the typical resource usage pattern in java – we acquire a resource (outside of the block – if we fail to acquire it, no close
is required), use it in the try
block, perform any desired exception handling in the catch
block, and then clean up (usually invoking the close
method on the object in question) in the finally
block, which is (roughly) guaranteed to be called, regardless of what happens during try
and catch
.
Frequently the close
method is marked as throwing a checked exception. This is annoying, as it means that even though we’ve performed some resource cleanup, our finally
clause must also handle that exception type (or our function must propagate it upwards). Typically, we get around this by writing a closeQuietly
method (or using one from an appropriate apache library) that catches the exception, suppresses it, then perhaps logs that a resource has failed to close.
This is absolutely fine for single resource usages – like reading all the data out of a file, or performing an http request to a remote server.
A more complicated world
Why’d you have to go and make things so complicated?
— Avril Lavigne
Commonly we will want to write applications that keep several resources of different species open for the lifetime of our application (or at least, considerably longer than a single function call). Perhaps a collection of sockets, some shared memory accessed through JNI and a sprinkling of semaphores.
What happens when we want to cleanly stop our application? These different resources will presumably be looked after by different pieces of our object graph, so we will have to traverse that graph calling close
where appropriate.
In a perverse world, our code might look like this:
public static void main(String[] args) { final SharedMemory memory = new SharedMemory(); final CustomSocket customSocket = new CustomSocket(); try { runApplication(memory, customSocket); } catch (Exception e) { e.printStackTrace(System.err); } finally { try { memory.close(); } catch (SharedMemoryException e) { e.printStackTrace(System.err); } try { customSocket.close(); } catch (SocketCloseException e) { e.printStackTrace(System.err); } } }
This may be a rather extreme example. Neither resource implements java.io.Closeable
(which would make things easier as we could extract a single method), and both give feedback only in the form of a checked exception. I’ve left the try/catch blocks in place to illustrate just how annoying this is. How much worse this gets per different resource species is left as an exercise to the reader – one hopes it is obvious that this is another place that exception throwing fails to scale.
An alternative approach
We could mandate extension of Closeable
for all such resources, but all this buys us is the ability to have just one static closeQuietly
function. That would be a start. Can we do better though?
Groans from the crowd. Is this another anti-exception post? You bet it is. Let’s consider this alternative interface:
package net.digihippo; import java.util.Collection; public interface Closeable { public Collection<? extends Exception> close(); }
A couple of things to note here before we continue.
- The interface isn’t actually that important – here’s the previous program with resources that follow this pattern without implementing the interface:
public static void main(String[] args) { final SharedMemory memory = new SharedMemory(); final CustomSocket customSocket = new CustomSocket(); try { runApplication(memory, customSocket); } catch (Exception e) { e.printStackTrace(System.err); } finally { final List<Exception> closeProblems = new ArrayList<Exception>(); closeProblems.addAll(memory.close()); closeProblems.addAll(customSocket.close()); for (final Exception e: closeProblems) { e.printStackTrace(System.err); } } }
Exception
may not be the type that we want here. Once we have a return type, it’s possible that we just want aCollection
ofString
messages that tell us whyclose
has failed, rather than the considerably more expensiveException
. I chose it mostly because it was nearest, and possibly because having the stack could be useful for post-hoc rationalization.
These particular resources, in our example, share the program’s lifetime, and so the only customer for the close
mechanism is the programmer trying to correctly order a whole bundle of unrelated shutdown
and close
calls. We will make their life a complete misery if we take the lazy option of failing to close
by throwing – what on earth do we expect our shutdown writer to do with it?
Conclusion
For resource library creators
C++ has given us a good hint – throwing from close
functions (and other functions in that family, like stop
and shutdown
) is unhelpful. In particular, throwing a custom checked exception type from a close
method verges on malicious. It makes writing shutdown code tedious, and tedium is not a fate we should wish on anyone. We should do our brave shutdown hook writers a favour and provide our error output in a sanitized format. If we really, really must throw (and by goodness we need a superb excuse to do so), implementing java.io.Closeable
is mandatory.
For shutdown writers
We are in a pretty poor place right now. From a brief scan of libraries I use, those pesky resource library creators (Presumably this includes the author? – Ed) have been out to get us – almost all of their close
methods throw checked exception types that we have no hope of recovering from. Try to wrap their laziness as close to the source as possible; who knows, perhaps the idea will take off, and our lives infinitesimally improve.
Postscript
Having written no C++ for some time, I fashioned the top example without having to look up what header std::runtime_error
was in, and the program compiled and ran at the first attempt. Get in. Admittedly, it didn’t do what I thought it would (my Foo
was optimized away), but even that small victory made me smile. Having written this rather smug post-script I now eagerly await -Wpedantic
like feedback from less rusty C++ writers!