Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Raising exception in destructor

251 views
Skip to first unread message

Jens Gruschel

unread,
Feb 23, 2005, 8:00:17 AM2/23/05
to
I always thought raising exceptions in destructors is a bad habbit. Now
I found that TCustomWinSocket.Destroy (ScktComp.pas) calls Cleanup,
which may raise an exception. Other things are cleaned up afterwards
(FreeMem(FGetHostData) is called). So my question is: what happens if an
exception is raised inside of a destructor? Is the object destroyed
properly? Is code following "raise ..." executed?

Jens

Martin James

unread,
Feb 23, 2005, 8:18:43 AM2/23/05
to
Yeah - I've often wondered about this too. If a destructor is freeing
stuff and, say, one of the objects has already been freed in a method
somewhere, this would raise an exception. Control then leaves the destructor
at an unknown point and an exception handler does not know which sub-objects
were freed and which not. Presumably, the object is still hanging around in
a half-destroyed state because the 'inherited' call at the end of the
destructor was never reached.

Rgds,
Martin


Jens Gruschel

unread,
Feb 23, 2005, 8:30:20 AM2/23/05
to
> Presumably, the object is still hanging around in
> a half-destroyed state because the 'inherited' call at the end of the
> destructor was never reached.

Just an idea...

destructor TMyObject.Destroy;
begin
try
ThisMightRaiseAnException;
finally
inherited;
end;
end;

But then again, if the inherited destructor is implemented in this way,
too, and both destructors raise an exception, what happens? Generally
spoken: What happens if an exception is raised in the finally section of
a try-block? Two exceptions pending at the same time is impossible,
isn't it?

Jens

valentin tihomirov

unread,
Feb 23, 2005, 9:28:57 AM2/23/05
to
I'm following this topic with a great interest.


Martin James

unread,
Feb 23, 2005, 9:36:58 AM2/23/05
to
> I'm following this topic with a great interest.

<g> Yes, I seem to remember a thread about this, or a similar, subject some
months ago.

Rgds,
Martin


Martin James

unread,
Feb 23, 2005, 9:51:42 AM2/23/05
to
>
> Just an idea...
>
> destructor TMyObject.Destroy;
> begin
> try
> ThisMightRaiseAnException;
> finally
> inherited;
> end;
> end;

Better, yes. Does not help with:

destructor TMyObject.Destroy;
begin
try
ThisMightRaiseAnException;

bigComplexObject.free;
finally
inherited;
end;
end;

You could nest the try-finallies to try & force the execution of every line
in every destructor in the hierarchy, but this seems very messy.

Maybee TObject should have an 'onDestroyFail' event that is called whenever
there is a problem in a destructor, rather than raising an exception in the
usual way. This might allow control to return to the destructor that had
experienced the problem & so finish cleaning up its other objects and
calling 'inherited'.

> But then again, if the inherited destructor is implemented in this way,
> too, and both destructors raise an exception, what happens?

Err.. hmm.. maybee time for an experiment :)

> Generally
> spoken: What happens if an exception is raised in the finally section of
> a try-block? Two exceptions pending at the same time is impossible,
> isn't it?

Not at all sure..

Rgds,
Martin


valentin tihomirov

unread,
Feb 23, 2005, 10:40:16 AM2/23/05
to

> <g> Yes, I seem to remember a thread about this, or a similar, subject
some
> months ago.

Yes, I had a stupid question on rollbacks. This is sorta different. In fact,
i suppose an administrator intervention is required to resolve the stalls
when you cannot neither proceed nor get back. This situaton must be
especially thoroughly polished in banking transaction theory due to
catastrophic consequencies of misbihaviour and distributed nature of the
systems (connection is not a reliable medium). So far, I have been satisfied
by this responce:
http://groups-beta.google.com/group/microsoft.public.win32.programmer.kernel/msg/1af76c303bc16eac?dmode=source .
The idea that the release routines must always succeed looks attractive but
not realistic. So, I waiting for links on more elaborative dissretations.


Avatar Zondertau

unread,
Feb 23, 2005, 10:59:09 AM2/23/05
to
> > But then again, if the inherited destructor is implemented in this
> > way, too, and both destructors raise an exception, what happens?
>
> Err.. hmm.. maybee time for an experiment :)
>
> > Generally
> > spoken: What happens if an exception is raised in the finally
> > section of a try-block? Two exceptions pending at the same time is
> > impossible, isn't it?
>
> Not at all sure..

It's easy to test. Running the following code you'll find out the
original exception is forgotten:

procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('Exception 1');
finally
raise Exception.Create('Exception 2');
end;
except
on E: Exception do
ShowMessage(Format('%s exception: "%s"', [E.ClassName,
E.Message]));
end;
end;

Craig Stuntz [TeamB]

unread,
Feb 23, 2005, 11:21:53 AM2/23/05
to
valentin tihomirov wrote:

> This is sorta different. In fact,
> i suppose an administrator intervention is required to resolve the
> stalls when you cannot neither proceed nor get back. This situaton
> must be especially thoroughly polished in banking transaction theory
> due to catastrophic consequencies of misbihaviour and distributed
> nature of the systems (connection is not a reliable medium).

Well, if you want a *really* theory-based explanation of this problem,
read:

http://research.microsoft.com/pubs/ccontrol/

-Craig

--
Craig Stuntz [TeamB] . Vertex Systems Corp. . Columbus, OH
Delphi/InterBase Weblog : http://blogs.teamb.com/craigstuntz
Everything You Need to Know About InterBase Character Sets:
http://blogs.teamb.com/craigstuntz/articles/403.aspx

Craig Stuntz [TeamB]

unread,
Feb 23, 2005, 11:24:40 AM2/23/05
to
Jens Gruschel wrote:

> I always thought raising exceptions in destructors is a bad habbit.

I agree:

http://blogs.teamb.com/craigstuntz/archive/2004/08/24/ATrickQuestionAndA
nImportantLesson.aspx

--
Craig Stuntz [TeamB] · Vertex Systems Corp. · Columbus, OH
Delphi/InterBase Weblog : http://blogs.teamb.com/craigstuntz

Craig Stuntz [TeamB]

unread,
Feb 23, 2005, 12:56:49 PM2/23/05
to
Jens Gruschel wrote:

> Very interesting. So when an error can occur in a destructor (like in
> TCustomWinSocket.Destroy) it's probably better to ignore it silently
> ("Windos can not clean up my socket? Not my problem. I don't need it
> any more.").

I can't comment on that specific case since I'm not really familiar
with the component.

--
Craig Stuntz [TeamB] · Vertex Systems Corp. · Columbus, OH
Delphi/InterBase Weblog : http://blogs.teamb.com/craigstuntz

Want to help make Delphi and InterBase better? Use QC!
http://qc.borland.com -- Vote for important issues

Jens Gruschel

unread,
Feb 23, 2005, 12:40:48 PM2/23/05
to
>>I always thought raising exceptions in destructors is a bad habbit.
>
> I agree:
>
> http://blogs.teamb.com/craigstuntz/archive/2004/08/24/ATrickQuestionAndA
> nImportantLesson.aspx

Very interesting. So when an error can occur in a destructor (like in

TCustomWinSocket.Destroy) it's probably better to ignore it silently
("Windos can not clean up my socket? Not my problem. I don't need it any
more.").

Jens

Rob Kennedy

unread,
Feb 23, 2005, 2:05:06 PM2/23/05
to
Jens Gruschel wrote:
>> Presumably, the object is still hanging around in
>> a half-destroyed state because the 'inherited' call at the end of the
>> destructor was never reached.

Correct. The destructor does not finish running, and the memory for the
object is not released. You'll have a memory leak.

> Just an idea...
>
> destructor TMyObject.Destroy;
> begin
> try
> ThisMightRaiseAnException;
> finally
> inherited;
> end;
> end;

Since the exception does not get handled, the destructor will not finish
running. The memory is *not* released by the inherited destructor. Just
as an object is already fully allocated before any code in a constructor
runs, the object is not de-allocated until after all code in all
destructors runs.

At the end of each destructor is some hidden code, generated by the
compiler, that checks whether this is the out-most destructor for the
object. If it is, then special de-allocation code is called.

> But then again, if the inherited destructor is implemented in this way,
> too, and both destructors raise an exception, what happens? Generally
> spoken: What happens if an exception is raised in the finally section of
> a try-block? Two exceptions pending at the same time is impossible,
> isn't it?

Not impossible at all. Only one is active, though. The exceptions are
stored on a stack. You're allowed to have try-except and try-finally
blocks nested within the except and finally sections of other blocks,
and it all works just as you'd expect it to.

If an exception is raised in an except or finally block and not caught
before escaping the outer nested block, then the previous exception is
lost forever. I think the exception gets freed, but it is not accessible
to any other code.

In the following code, exception A will get caught by the inner except
block, but it will get discarded when B gets raised. The outer except
block catches B. Exception A gets freed, although I'm not certain of
when that happens.

try
try
raise Exception.Create('A');
except
on A: Exception do begin
S := TStringList.Create;
try
raise Exception.Create('B');
finally
S.Free;
end;
raise;
end;
end;
except
on B: Exception do Writeln(B.Message); // prints 'B'
end;

If the try-finally block had been a try-except block, then it would
catch and handle exception B. When execution reached the final "raise"
statement, exception A would get re-raised, and the outer except block
would catch A instead of B.

--
Rob

0 new messages