[comp.lang.c++] Exceptions and Destructors

mikem@otc.oz (Mike Mowbray) (04/08/88)

Since there's heaps of talk goign around about exception-handling ....

We've heard about how when an exception is raised, and a function context
is resumed further back in the stack, appropriate destructors must first
be called for stuff inside intermediate functions which will now not be
returned through.

Problem: this means that all classes must be designed so that their
	 destructors are callable at any instant and behave correctly, even
	 if you're in the middle of a member function which is adjusting
	 the internal configuration of the class. (For example, a member
	 function which calls another function in which an exception gets
	 raised).

In general, it would seem very demanding to require that destructors for
non-trivial classes be callable at any moment. The destructor writer must
anticipate every possible mis-configuration of the class's internal
structure.

Maybe it's sufficient to adopt a general rule: "In your destructor, assume
that everything is probably corrupted". But then this must also apply to
member functions, since destructors might call them to do part of the
cleanup job. In any case, this is obviously highly error-prone.

The alternative is to ensure that a member function never calls something
else unless the class is already perfectly consistent. But how could an
automated tool assist in detecting this? Maybe the programmer could define
assertions for the class, and the compiler would arrange for them to be
checked before calling any function from within a member function. Sounds
dreadful, doesn't it. Maybe it would be sufficient for the mechanism that
generates the automatic call of destructors to arrange for the assertions
to be checked. But what do you do if the assertion fails?

Another problem area: what if a destructor executes normally to some point,
and then calls a function in which an exeption gets raised, which invokes
the same destructor again in the attempt to clean up properly...?

Thoughts anyone?

			Mike Mowbray
		    Systems Development
			|||| OTC ||

PHONE	(02) 287-4104			  ACSnet:  mikem@otc.oz
FAX	(02) 287-4990			    UUCP:  {uunet,mcvax}!otc.oz!mikem 
SNAIL	OTC, GPO Box 7000		   CSNET:  mikem@otc.oz.au
	Sydney 2001, Australia		    ARPA:  mikem@otc.oz.au

shapiro@blueberry.inria.fr (Marc Shapiro) (04/19/88)

In article <352@otc.oz> mikem@otc.oz (Mike Mowbray) writes:
>Since there's heaps of talk goign around about exception-handling ....
> [...]
>Problem: this means that all classes must be designed so that their
>	 destructors are callable at any instant and behave correctly, even
>	 if you're in the middle of a member function which is adjusting
>	 the internal configuration of the class. (For example, a member
>	 function which calls another function in which an exception gets
>	 raised).
>
>In general, it would seem very demanding to require that destructors for
>non-trivial classes be callable at any moment. The destructor writer must
>anticipate every possible mis-configuration of the class's internal
>structure.
>[...]
>Another problem area: what if a destructor executes normally to some point,
>and then calls a function in which an exeption gets raised, which invokes
>the same destructor again in the attempt to clean up properly...?

I agree totally with these points.  I would also like to add
that you are never assured that the destructor for a certain object
will actually be called: you might exit, abort, call a non-returning
function, or just forget to call delete.

Therefore I think an important programming principle is: NEVER TRUST A
DESTRUCTOR TO BE CALLED.  (Does Unix trust you to "close()" all your files
cleanly?  No way.)

Corollary: NEVER DO ANYTHING IMPORTANT IN A DESTRUCTOR.  The only
sorts of things a destructor should do are "benevolent side-effects",
i.e. things which can benefit performance but won't be a mistake if
not performed, e.g. freeing memory.

Suppose for instance you have an application which creates many
windows.  For each window the application allocates a window object.
The window object constructor tells the window manager to reserve
space for it, and the desctructor tells the window manager to close
(i.e. erase) the window.

Now what if this application gets killed: the window manager must
detect this and delete the corresponding windows.  Conclusion:
it's not worthwile for the window-object destructors to close windows,
it's just duplicating work that the manager has to do anyway.  (If you
want windows to pop up and disappear, add a window::close() operation,
independent of the destructor).

The final problem is that the window manager needs a way to detect
that that the application went away.  This is not easy in today's
operating systems.


						Marc Shapiro

INRIA, B.P. 105, 78153 Le Chesnay Cedex, France.  Tel.: +33 (1) 39-63-53-25
e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro

						Marc Shapiro

INRIA, B.P. 105, 78153 Le Chesnay Cedex, France.  Tel.: +33 (1) 39-63-53-25
e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro

jima@hplsla.HP.COM ( Jim Adcock) (04/21/88)

In general, it seems to me your statements are true, iff one
requires that "exception handling" be able to handle any kind
of failure, at any point of time in the execution of the 
software.

In many cases, you have a better knowledge of where and when
to expect various kinds of errors to crop up, in which case
possibly one could pragmatically apply easier requirements
to one's error handling, recovery, and associated destructor
schemes.

I don't believe "exception handling" is going to magically
"fix" all the software/hardware errors that can crop up 
in a serious-sized software project.

So I believe an "exception handling" scheme that can improve
the overall reliability of the software product by a factor
of maybe 10X-100X or better would be quite acceptible.