473,779 Members | 1,884 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Destructors are useless?

I've been having problem with destructors in the context of having ported C# code
developed under .NET to Mono. What happens is that, on a dual-CPU machine, various
parts of the code crash randomly (and rarely). This always happens during
process shutdown, after some thread has called System.Environm ent.Exit(). Clearly,
some sort of race condition.

Note that what follows only applies to destructors that are called when the process
shuts down -- there is no problem with destructors that get called by the GC when
the process is executing normally.

After a lot of debugging, I tracked it down to a destructor along the following
lines:

~SomeClass
{
if (!_destroyed)
{
System.Console. Error.WriteLine ("Forgot to destroy SomeClass");
}
}

Under Mono, the attempt to write to the console crashes the process because, by the time
destructor is called by the garbage collector, the I/O subsystem has been partially
garbage collected already, and the process dies with a NullPointerExce ption somewhere
in the guts of the I/O subsystem.

Not a problem with .NET: in .NET, the console isn't destroyed until after everything
else is destroyed. (For details, see:
http://www.bluebytesoftware.com/blog...3-20c06ae539ae
There is a comment about two-thirds of the way down the page by Brian Grunkemeyer to that effect.)

Except that I can't rely on this because, as far as I can see, the spec doesn't guarantee that
the console will hang around during process shutdown, so it's not portable code.

Then I started reading the spec a bit more and found that destruction order is not guaranteed and
that, if A refers to B, it's entirely possible for B to be finalized before A. So, that got me to
thinking about what is actually legal to do from within a destructor, if that destructor may be
called during process shutdown. Here is a list of things that are *not* legal to do:

- I cannot dereference anything. If I do, the memory for the object that is reference is guaranteed
to still be there. However, that object may have been finalized already and, as a result, may no
long be in working order if I call a method on it.

- I cannot call a static method on anything. The static method itself is guaranteed to be there. However, the
implementation of the static method may depend on another static object that has been finalized already.
See previous point.

- I cannot safely invoke a virtual method on my own object. That's because my own object may have a derived
part, and that derived part may have been finalized already, and the virtual method may end up using
something in the derived part that conceptually no longer exists.

So, that doesn't leave a lot I can do in a destructor, as far as I can see. Here is what I can do safely:

I can assign or read any of my own data members, and any of the accessible data members of my base class.
That's about it.

So, I can assign null to all my data members that have reference type, just to be nice to the GC. But that
really isn't all that essential in most circumstances.

I can read my own data members. To what purpose? Well, to assert that my program state is still in fine
shape, of course:

~SomeClass()
{
System.Diagnost ics.Assert(_myM ember != null);
System.Diagnost ics.Assert(_myO therMember == null);
}

Oops. I can't safely call a static method, because whatever the implementation of the static method uses may
have been finalized already. Just as I can't safely write to the console, I can't safely assert either.

Of course, I have no real control over when destructors are called. In particular, I have not control
over what destructors run when some thread calls Exit(). As a result, these restrictions apply to *all*
destructors, not just those of static objects.

Hmmm... That leaves destructors completely useless. There is nothing, not even asserting, that I can do safely.
Of course, that begs the question: why have destructors when I can't do anything with them? As far as I can
see, the only legal statements inside a destructor are effectively no-ops.
Mystified,

Michi.
Nov 17 '05
26 2725

Isn't this a bug in the MONO runtime?

Debug.Assert throws, the mono-runtime catches that and logs it to stderr
(i guess) but stderr is closed.


I don't think it's a bug in Mono because, as far as I can see, the spec
doesn't guarantee that either writing to the console or doing an assert
is safe within a finalizer. After all, the spec quite clearly says
that the order of finalization is undefined. So, given that, I don't think
I can expect assert to work correctly during process shutdown.
After all, the interals that assert needs to do its job may well have been
finalized already.

(Of course, it would be very nice if it would be guaranteed to work...)

Cheers,

Michi.

Nov 17 '05 #21
"Helge Jensen" <he**********@s log.dk> wrote in message
news:eK******** *****@TK2MSFTNG P12.phx.gbl...

The stack trace was obtained when the object was created and stored in
the object, roughly as:

class Foo: IDisposable {
StackTrace t; // Construction captures stack-trace
bool disposed;
...
~Foo() {
if ( !disposed )
throw new MissingDispose( t);
}
}
Ah, I get you now. No problem with that, of course.
However, what happens when the finalizer actually throws?
The spec says that exceptions during finalization are ignored
and abort execution of that finalizer. So, when the exception
is thrown, what actually happens? I would expect nothing
to happen at all from the words in the spec.
Obtaining the stack-trace at finalization time isn't very usefull, the
finalizer is run by the finalization thread.


Yes, true.
crashes the process. In other words, it's not safe to do try and do
this from within a finalizer.


The GC *can* invoke finalizers in undefined order, so you better not
rely on the functionality of an object that has a finalizer, inside your
finalizer.


Right. That's why writing to the console can crash the process under Mono:
the console is finalized before my finalizer tries to use it. (And I can't find
words in the spec that would make this behavior of Mono illegal.)

Cheers,

Michi.

--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com

Nov 17 '05 #22

Michi Henning wrote:
If someone could point me to a guarantee in the spec as to the
usability of the Console from a finalizer, I'd be grateful!


By "spec" I am not clear exactly what documentation you are referring
to but the MSDN docs call it out here:
http://msdn.microsoft.com/library/de...artedtopic.asp

Admittedly I don't know why this is documented in
Environment.Has ShutdownStarted but here it is, reproduced verbatim:

"Note: An exception to this rule is the Console class, which contains
static fields that reference stream objects, but is implemented
specially so you can always write to the system console, even during
domain unloading or system shutdown."

Is this authoritative enough?

thanks
--Dilip

Nov 17 '05 #23
Michi Henning wrote:
I don't think it's a bug in Mono because, as far as I can see, the spec
doesn't guarantee that either writing to the console or doing an assert
Are you sure it's because System.Console is finalized? it's not actively
closed by the runtime when terminating the application?

Anyway, you can do GC.SuppressFina lize(System.Con sole) to prevent it
from being finalized. Note that you may need to SuppressFinaliz e some
more things to get through this (see below).
is safe within a finalizer. After all, the spec quite clearly says
that the order of finalization is undefined. So, given that, I don't think
I can expect assert to work correctly during process shutdown.


The order of finalization of objects "elegible for destruction" is
undefined, but hopefully System.Console isn't elegible for destruction
untill your object is collected.

Looking at Ecma TC39-TG2/2004/14, the relevant statement must be section
10.9-2 at p. 101:

"If no part of the object can be accessed by any possible continuation
of execution, other than the running
of destructors, the object is considered no longer in use, and it
becomes eligible for destruction. [Note:
Implementations might choose to analyze code to determine which
references to an object can be used in
the future. For instance, if a local variable that is in scope is the
only existing reference to an object, but
that local variable is never referred to in any possible continuation of
execution from the current
execution point in the procedure, an implementation might (but is not
required to) treat the object as no
longer in use. end note]"

I have previously interpreted "by any possible continuation of
execution, other than the running of destructors" as disregarding code
actually in destructors, not calls to external functions. This would
prevent System.Console from being "elegible for destruction".

Unfortunatly, the formulation can also be read as "you can disregard any
references in code run in destructors, directly or indirectly" when
determining what is "elegible for destruction", which *would* let
System.Console be "elegible for destruction" before your destructor is run.

I had not considered this interpretation before, It would be nice to
know if this was the intended meaning. Especially, this interpretation
would allow the GC to actually *collect*, not only finalize, objects
only referenced (in code) in destructors at *any* time. I have a hard
time believing that is the intention.

I can see that this interpretation is required to be able to guarantee
that the finalizer can be run for all objects, otherwise cyclic
dependent finalizers would never render an object "elegible for
destruction", but really...
--
Helge Jensen
mailto:he****** ****@slog.dk
sip:he********* *@slog.dk
-=> Sebastian cover-music: http://ungdomshus.nu <=-
Nov 17 '05 #24
"Dilip" <rd*****@lycos. com> wrote in message
news:11******** ************@l4 1g2000cwc.googl egroups.com...
Admittedly I don't know why this is documented in
Environment.Has ShutdownStarted but here it is, reproduced verbatim:

"Note: An exception to this rule is the Console class, which contains
static fields that reference stream objects, but is implemented
specially so you can always write to the system console, even during
domain unloading or system shutdown."
Yes, I stumbled across this one too.
Is this authoritative enough?


Well, for .NET, it is. But it's not authoritative enough for Mono, I suspect.
Guarantees such as this should really be part of the language or library
spec. Anyway, I'll point the Mono people at that passage, thanks!

Cheers,

Michi.
--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com

Nov 17 '05 #25

Helge Jensen wrote:
Michi Henning wrote:

Are you sure it's because System.Console is finalized? it's not actively closed by the runtime when terminating the application?

Anyway, you can do GC.SuppressFina lize(System.Con sole) to prevent it
from being finalized. Note that you may need to SuppressFinaliz e some more things to get through this (see below).
Maybe the OP could detect at runtime whether his app is running under
Mono or .NET and decide based on that whether to suppress finalization
for the console classes? That might give some uniformity, no?
Looking at Ecma TC39-TG2/2004/14, the relevant statement must be section 10.9-2 at p. 101:


I took a look at this. Like you I couldn't find any literature that
explicitly says Console classes are ok to be used from inside a
finalizer. However, the examples that follow section 10, explaining
the finalization order of objects implicitly do a Console.WriteLi ne
from inside the finalizer. Maybe we should take it to mean they are
ok?

--Dilip

Nov 17 '05 #26
Dilip wrote:
Looking at Ecma TC39-TG2/2004/14, the relevant statement must be


section
10.9-2 at p. 101:

I took a look at this. Like you I couldn't find any literature that
explicitly says Console classes are ok to be used from inside a
finalizer. However, the examples that follow section 10, explaining
the finalization order of objects implicitly do a Console.WriteLi ne
from inside the finalizer. Maybe we should take it to mean they are
ok?


Hmmm... Following the letter of the spec, I think the code examples
in the spec violate its own rules:

"If the object, or any part of it, cannot be accessed by any possible
continuation of execution, other than the running of destructors, the
object is considered no longer in use, and it becomes eligible for
destruction."

Once a process calls Exit(), the only code that can still run is the code
in destructors. This means that System.Console is now only accessible
from destructors and is eligible for destruction. Because destructors
can run in any order, it's perfectly legal for System.Console to be
finalized before some application objects are finalized. So, the code
in the spec appears to be, stricly speaking, illegal:

using System;
class A
{
~A() {
Console.WriteLi ne("Destruct instance of A");
}
}

In particular, the spec states at the end of section 10.9:

"To avoid confusion and unexpected behavior, it is generally a good
idea for destructors to only perform cleanup on data stored in their
object’s own fields, and not to perform any actions on referenced objects
or static fields."

But, of course, the examples in the spec do exactly what the spec says
you shouldn't do because it calls a static member function which, in
turn, accesses static fields.

And, as far as assertions are concerned, the same thing holds, of course.

It would be *really* nice to be able to assert within a destructor. I'd
like to write destructors such as

~SomeObject()
{
Debug.Assert(_c ond1);
Debug.Assert(_c ond2);
Debug.Assert(_c ond3);
Debug.Assert(_c ond4);
Debug.Assert(_c ond5);
Debug.Assert(_c ond6);
Debug.Assert(_c ond7);
}

and, if one of the assertions fails, I'd like to which assertion it
was that triggered.

And, by special-casing System.Console, .NET goes beyond the guarantees
provded by the spec, which isn't a good idea because it creates portability
problems, as evidenced when using such destructors under Mono on multi-CPU
machines.

Cheers,

Michi.
Nov 17 '05 #27

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

16
5293
by: David Turner | last post by:
Hi all I noticed something interesting while testing some RAII concepts ported from C++ in Python. I haven't managed to find any information about it on the web, hence this post. The problem is that when an exception is raised, the destruction of locals appears to be deferred to program exit. Am I missing something? Is this behaviour by design? If so, is there any reason for it? The only rationale I can think of is to speed up...
9
1643
by: David Turner | last post by:
Further to the discussion from earlier titled "does python have useless destructors?", I've posted a brief summary of garbage collection, RAII, and the "using" clause (which is what PEP310's "with" clause amounts to). The post is here: http://dkturner.blogspot.com/2004/06/garbage-collection-raii-and-using.html Regards David Turner
6
2185
by: Elbert Lev | last post by:
Please correct me if I'm wrong. Python (as I understand) uses reference counting to determine when to delete the object. As soon as the object goes out of the scope it is deleted. Python does not use garbage collection (as Java does). So if the script runs a loop: for i in range(100): f = Obj(i)
3
21393
by: Rajesh Garg | last post by:
Can we have private constructors and destructors? IF yes what is the use of such constructors or destructors.....in the sense where can these be implemented in a system................. I have an idea that we can have private constructors and destructors but am not able to find a situation where they can be used... Regards RVG rajeshgarg@opussoft.com
8
1807
by: Edward Diener | last post by:
I have a __value class which uses some legacy C++ code. So I wrapped the legacy C++ code in another __nogc class and have a pointer to that class as a member of my __value class. When the __value class is created, I dynamically allocate an object of the class with the legacy C++ code. However because the __value class has no destructor, I can never release that allocated memory. Why does a __value class allow no destructor ? Without it I...
3
1895
by: alex.gman | last post by:
If I have code like this int f() { // ... stuff ... g(); if(x > 0) return (x+4); // ... more stuff ... always_call(z); return y; }
6
7531
by: mlw | last post by:
Could someone explain why there is no destructor in Java classes? There are many times you need to be called WHEN an object goes out of scope and not when it will eventally be freed.
5
2085
by: Rennie deGraaf | last post by:
I know that if an exception is thrown from a destructor while unwinding the stack because of another exception, terminate() is called (FAQ 17.3). How exactly does this rule work? Is it acceptable to both throw /and/ catch an exception inside a destructor, as in the following code? struct Foo { void finalize() { throw 1.0;
6
5166
by: Jeff Newman | last post by:
Hello, Could anyone explain to me why the following class's destructor shows up as having multiple branches? (At least as judged by gcov 4.1.2 when compiled with gcc 4.1.2 ): struct blah { blah(); virtual ~blah();
0
9632
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
10302
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9925
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8958
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6723
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5372
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5501
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4036
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3631
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.