473,778 Members | 1,910 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 2724
"Helge Jensen" <he**********@s log.dk> wrote in message
news:42******** ******@slog.dk. ..
Michi Henning wrote:
"Helge Jensen" <he**********@s log.dk> wrote in message

Hmmm... The specification says that, if a destructor throws, the exception
is ignored and execution of that destructor is aborted. Given that you cannot
So conforming runtimes should ignore the exception, that way semantics
is preserved.
safely assert in a destructor or write to the console, it seems unlikely
that you could safely record a stack trace: after all, the classes you have to

use
It works well in practice, especially in debug mode.
to record the stack trace may have long since been finalized. And, if you


But i still have a trace indicating the stack that allocated the object
that wasn't Dispose'ed, that's a whole lot better than knowing nothing,
or just it's type.


I think we are missing each other here. I'm not disputing that a stack trace
would be useful. It clearly would be. (Just as an assert would be.) But,
by the time a finalizer runs as a result of a process calling Exit(), the run
time may well be in a state where the act of trying to get a stack trace
crashes the process. In other words, it's not safe to do try and do
this from within a finalizer.

Cheers,

Michi.

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

Nov 17 '05 #11

Michi Henning wrote:

I think we are missing each other here. I'm not disputing that a stack trace would be useful. It clearly would be. (Just as an assert would be.) But, by the time a finalizer runs as a result of a process calling Exit(), the run time may well be in a state where the act of trying to get a stack trace crashes the process. In other words, it's not safe to do try and do
this from within a finalizer.


Michi

I think you answered your own question. There is a school of thought
in the .NET community that pretty much agree with your assessment --
that a finalizer is almost completely useless. Brent Rector, who is
pretty well known in the .NET/MSFT circles, clearly shares your view:
http://www.developer.com/net/csharp/article.php/2233111

My suggestion is -- why don't you tell tell us exactly what you want to
do in a finalizer in your specific case? If we deal with concretes
instead of hypotheticals we may be able to reason why that is either
possible or not possible.

thanks!
--Dilip

Nov 17 '05 #12
It is interesting that Java doesn't have this problem. On process exit, Java
guarantees
that destructors (or finalizers, if you prefer) will *not* run. This means that
it's perfectly
safe to put assertions into Java finalizers: while the process is alive and
running, the
assertions are checked whenever the GC decides to garbage collect an object.
And, if the process is exiting, destructors don't run at all, meaning that
there are
no issues with respect to destruction order. Of course, that means that
assertions
don't run during process shut down, but at least my process gets to shut down
without crashing.

If that's the behavior you want, how about

~SomeClass()
{
if ( !Environment.Ha sShutdownStared ) {
Debug.Assert( ... );
}
}


Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
Nov 17 '05 #13
"Mattias Sjögren" <ma************ ********@mvps.o rg> wrote in message
news:%2******** ********@TK2MSF TNGP14.phx.gbl. ..
It is interesting that Java doesn't have this problem. On process exit, Java
guarantees
that destructors (or finalizers, if you prefer) will *not* run. This means thatit's perfectly
safe to put assertions into Java finalizers: while the process is alive and
running, the
assertions are checked whenever the GC decides to garbage collect an object.
And, if the process is exiting, destructors don't run at all, meaning that
there are
no issues with respect to destruction order. Of course, that means that
assertions
don't run during process shut down, but at least my process gets to shut downwithout crashing.

If that's the behavior you want, how about

~SomeClass()
{
if ( !Environment.Ha sShutdownStared ) {
Debug.Assert( ... );
}
}


I just stumbled across this too, this looks interesting -- I'll experiment with
that, thanks!

Cheers,

Michi.

Nov 17 '05 #14
"Mattias Sjögren" <ma************ ********@mvps.o rg> wrote in message
news:%2******** ********@TK2MSF TNGP14.phx.gbl. ..

If that's the behavior you want, how about

~SomeClass()
{
if ( !Environment.Ha sShutdownStared ) {
Debug.Assert( ... );
}
}


Unfortunately, I don't see how I can make that thread-safe --
HasShutDownStar ted may change value half-way through
the execution of some finalizer.

Cheers,

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

Nov 17 '05 #15
"Dilip" <rd*****@lycos. com> wrote in message
news:11******** **************@ g14g2000cwa.goo glegroups.com.. .
Michi

I think you answered your own question. There is a school of thought
in the .NET community that pretty much agree with your assessment --
that a finalizer is almost completely useless. Brent Rector, who is
pretty well known in the .NET/MSFT circles, clearly shares your view:
http://www.developer.com/net/csharp/article.php/2233111

My suggestion is -- why don't you tell tell us exactly what you want to
do in a finalizer in your specific case? If we deal with concretes
instead of hypotheticals we may be able to reason why that is either
possible or not possible.


Fair enough :-)

I don't want to do all that much, really. My finalizers are side-effect
free as far as the application is concerned. All I really want to do
is be able to safely assert within a finalizer and, if the assertion fails,
get a reliable stack trace instead of a crash. The only other requirement
is that the finalizer must be able to obtain a lock on its own object because
I'm running multi-threaded, but I don't see any problem there. So, the
typical scenario is:

class SomeClass()
{
// Lots of member functions here...

public void someMethod()
{
lock(this)
{
_mem3 == 99;
// ...
}
}

~SomeClass()
{
lock(this)
{
Debug.Assert(_m em1 == null);
Debug.Assert(_m em2 != null);
Debug.Assert(_m em3 == 99);
// etc...
}
}
private someType _mem1;
private someType _mem2;
private int _mem3;
}

Ideally, I'd like the assertions to be tested not just during normal
program execution, but also when the process goes away. Apparently,
that's impossible because just about anything I do from within a finalizer
may end up crashing the process, depending on the order in which things
are finalized.

I'm going to experiment with testing for whether process
shutdown has started to protect the finalizer from doing someting potentially
lethal. The downside of this is that assertions won't be checked during process
shutdown. And it's not clear to me yet whether that approach is guaranteed
free from race conditions. For example, what happens if I'm multi-threaded,
and a thread calls Exit() while some other thread is half-way through a
finalizer,
and GC kicks in in parallel? On a multi-CPU machine, the GC thread will run
truly asynchronously to application threads. So, potentially, I think the
sequence
of events could be:

~SomeClass()
{
lock(this)
{
// Test whether process is shutting down and skip assertions in that
case
if(!processIsGo ingAway)
{
Debug.Assert(_m em1 == null);
// Some other thread calls Exit() and GC kicks in here and
finalizes a pile
// of things, including whatever machinery is used by
Debug.Assert()
// This thread is suspended for the time being.

// This thread is scheduled again. While this thread was asleep,
the condition
// !processIsGoing Away has changed and no longer holds.

Debug.Assert(_m em1 != null); // BANG!!!

// I have a bug in my code, and _mem1 isn't non-null. But the
assert
// crashes my process because the GC, meanwhile, has finalized the
// infrastructure that is needed by Assert().
}
}
}

I think the above is entirely possible. Worse, it's likely to be rare, so the
problem
may go undetected for a long time.

Cheers,

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

Nov 17 '05 #16
Michi Henning wrote:
I think we are missing each other here.
Possibly.
I'm not disputing that a stack trace
would be useful. It clearly would be. (Just as an assert would be.) But,
by the time a finalizer runs as a result of a process calling Exit(), the run
time may well be in a state where the act of trying to get a stack trace
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);
}
}

Obtaining the stack-trace at finalization time isn't very usefull, the
finalizer is run by the finalization thread.
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.

In http://www.developer.com/net/csharp/article.php/2233111, this is
conservatively generalized to:

"you cannot, in general, use any member reference variables of an
object in its Finalize method."

Which is true, but not in the strict sense -- the "in general" saves
that. You can reference a member, but don't rely on any behaviour of it
that it's (possible) finalizer changes.

--
Helge Jensen
mailto:he****** ****@slog.dk
sip:he********* *@slog.dk
-=> Sebastian cover-music: http://ungdomshus.nu <=-
Nov 17 '05 #17
Michi,
No, don't think so. Under Mono, the code crashes with a
NullPointerExce ption
if the assertion fails somewhere inside the assert. A far as I can see,
the
Can it just be that Mono is non-conformant to the specification and it is a
particular Mono problem?
Are you able to repro this faulty behavior on Microsoft .NET ?

--
Sincerely,
Dmytro Lapshyn [Visual Developer - Visual C# MVP]
"Michi Henning" <mi***@triodia. com> wrote in message
news:%2******** ********@TK2MSF TNGP12.phx.gbl. .. "Dilip" <rd*****@lycos. com> wrote in message
news:11******** **************@ l41g2000cwc.goo glegroups.com.. .
Ok -- so how about this:

class someclass
{
~someclass()
{
try
{
System.Diagnost ics.Assert(_myM emVar == null);
}
catch(ObjectDis posedException)
{
// you can't do much here anyway
}
}
}


No, don't think so. Under Mono, the code crashes with a
NullPointerExce ption
if the assertion fails somewhere inside the assert. A far as I can see,
the
problem
is that, by the time my finalizer runs, stdout and stderr have been closed
already
and further calls to assert crash the process.

Cheers,

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


Nov 17 '05 #18
Michi Henning wrote:

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.

--
Helge Jensen
mailto:he****** ****@slog.dk
sip:he********* *@slog.dk
-=> Sebastian cover-music: http://ungdomshus.nu <=-
Nov 17 '05 #19
> Michi,
No, don't think so. Under Mono, the code crashes with a
NullPointerExce ption
if the assertion fails somewhere inside the assert. A far as I can see,
the


Can it just be that Mono is non-conformant to the specification and it is a
particular Mono problem?
Are you able to repro this faulty behavior on Microsoft .NET ?


No, not on .NET. Part of the problem may be that I don't have a dual-CPU
machine to run .NET on. With Linux/Mono, the problem only shows up
on a dual-CPU machine, but not when I boot that machine in single-CPU
mode.

I know that .NET special-cases the Console object so it's possible
to print something from a finalizer without running the risk of crashing.
But, as far as I can see, that guarantee is not made by the spec, only
by the .NET implementation. Hence, the Mono behavior is, strictly
speaking, legal.

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!

Cheers,

Michi.
Nov 17 '05 #20

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
1894
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
2084
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
9629
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
9470
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10298
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...
1
10069
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9923
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
8957
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...
1
7475
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
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();...
1
4033
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

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.