473,385 Members | 1,908 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

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.Environment.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 NullPointerException 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.Diagnostics.Assert(_myMember != null);
System.Diagnostics.Assert(_myOtherMember == 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 #1
26 2665
It is recommended to avoid using finalizers (they are actually called
'finalizers', not 'desctructors') wherever possible. If you need to release
unmanaged resources, implement IDisposable and follow the IDisposable
implementation design pattern recommended by Microsoft. If your class does
not use anything to be disposed, do not use the finalizer at all.

Still, you can put the finalizer to a good use implementing the Microsoft's
disposable object design pattern. In the pattern, the finalizer is the last
chance to release managed resources owned by the instance.

--
Sincerely,
Dmytro Lapshyn [Visual Developer - Visual C# MVP]
"Michi Henning" <mi***@zeroc.com> wrote in message
news:On**************@tk2msftngp13.phx.gbl...
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.Environment.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
NullPointerException 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.Diagnostics.Assert(_myMember != null);
System.Diagnostics.Assert(_myOtherMember == 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 #2
On Wed, 27 Apr 2005 17:40:26 +1000, Michi Henning wrote:
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.


If you are writing all managed code, then this statement is essentially it.
Within a pure managed environment this is no need to write a destructor.
Destructors are useful for releasing unmanaged resources. The garbage
collector has no specific knowledge on how to clean up an unmanaged
resource. Your class that encapsulates an unmanaged resource does have
that knowledge. So in your destructor you would take the responsibility
for making sure any unmanaged resources are properly released. Let the GC
handle your managed resources.

Also note that adding a destructor unnecessarily to an object can have a
negative impact on performance. An object with a destructor requires at
least two garbage collections. When the GC runs it reclaims the memory for
inaccessible objects without destructors. It cannot, at that time, collect
the inaccessible objects that do have destructors. Instead it places them
in a list of objects that are marked as ready for finalization. It then
calls the finalize method (destructor) on the objects in that list and then
removes them from the list. The next time GC runs it can them collect
these objects as they are now no longer in the list of objects that are
ready for finalization. So the fact that you have added a destructor to
your object only increases the chances that it will hang around longer than
other objects.
--
Tom Porterfield
Nov 17 '05 #3
Tom Porterfield wrote:
On Wed, 27 Apr 2005 17:40:26 +1000, Michi Henning wrote: If you are writing all managed code, then this statement is essentially it.
Well,... not quite, but perhaps that why you write "essentially"?
Within a pure managed environment this is no need to write a destructor.


Destructors can be quite usefull for checking that appropriate action
has been done on objects. For example you can check whether Dispose()
has been called.

The really usefull information: who forgot to call Dispose, is of course
not available, but I have a nice MixIn class that I can use to record
the stacktrace when the object is created and that is definatly also
usefull to know. It gives you a place to start looking.

While you may not be able to do much with the knowledge in your
finalizer, you can certainly throw an exception (the program *IS*
broken,... it forgot to call Dispose()) and the runtime may do
"good-things" with it. In debug mode the Visual IDE pops up a
messagebox, alerting you to a bug which would otherwise go unnoticed.

You can have your Dispose() call "GC.SuppressFinalize(this)" and avoid
invocation of the destructor.

--
Helge Jensen
mailto:he**********@slog.dk
sip:he**********@slog.dk
-=> Sebastian cover-music: http://ungdomshus.nu <=-
Nov 17 '05 #4
"Tom Porterfield" <tp******@mvps.org> wrote in message
news:7t**************@tpportermvps.org...
On Wed, 27 Apr 2005 17:40:26 +1000, Michi Henning wrote:
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.
If you are writing all managed code, then this statement is essentially it.


Yes, it sure looks like it.
Within a pure managed environment this is no need to write a destructor.
Destructors are useful for releasing unmanaged resources. The garbage
collector has no specific knowledge on how to clean up an unmanaged
resource. Your class that encapsulates an unmanaged resource does have
that knowledge. So in your destructor you would take the responsibility
for making sure any unmanaged resources are properly released. Let the GC
handle your managed resources.


Sure, I understand all that. A destructor can't do anything that relies on
another
object. In other words, a destructor can't dereference anything, and it cannot
even call a static function. Fine, I accept that. So what are destructors good
for?
Releasing unmanaged resources is one thing. (If I can accept that unmanaged
resources may not be released promptly.) But the *one thing* I still would
like to use destructors for is to check whether my program is still in a sane
state. In other words, I'd like to write assertions about my own member
variables. Unfortunately, I can't even do that: if the assertion holds, no
problem;
but, if the assertion fails, the mere act of having it fail is sufficient to
cause
undefined behavior. So, damned if I do, and damned if don't: if I don't assert,
my program has a bug and will go and do undefined things. If I do assert, the
act
of detecting that my program has a bug causes my program to go and do
undefined things. Talk about being caught between a rock and a hard place...

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.

And, of course, the idea that C# destructors are good for releasing unmanaged
resources seems flawed, too. After all, how many realistic programs are there
that can release unmanged resources without having to enrol the aid of some
other
managed resource? For example, in my particular case, the program crashed
because
of a destructor that looked like this:

class SomeClass
{
~SomeClass()
{
if(_destroyed)
{
_logger.warning("Forgot to destroy SomeClass");
}
}

private Logger _logger;
}

What I'm suggesting here is that, for realistic programs, chances are pretty
slim
that a destructor can clean up an unmanaged resource without having to ask for
help from some managed object. But, of course, as soon as that is the case,
the destructor cannot be used anymore. I'm left to conclude that destructors
are so useless that I can't even use them to make assertions about my own
member variables, let alone use them to reclaim unmanaged resources.

It truly would seem appropriate to remove destructors from the language
altogether. At least, that way, people wouldn't have their illusions shattered.

Of course, the alternative would be to not run destructors on process shutdown,
like Java does, and the problem would go away...

Cheers,

Michi.

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

Nov 17 '05 #5
"Helge Jensen" <he**********@slog.dk> wrote in message
news:uB**************@tk2msftngp13.phx.gbl...
The really usefull information: who forgot to call Dispose, is of course
not available, but I have a nice MixIn class that I can use to record
the stacktrace when the object is created and that is definatly also
usefull to know. It gives you a place to start looking.

While you may not be able to do much with the knowledge in your
finalizer, you can certainly throw an exception (the program *IS*
broken,... it forgot to call Dispose()) and the runtime may do
"good-things" with it. In debug mode the Visual IDE pops up a
messagebox, alerting you to a bug which would otherwise go unnoticed.


Hmmm... The specification says that, if a destructor throws, the exception
is ignored and execution of that destructor is aborted. Given that you cannot
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
to record the stack trace may have long since been finalized. And, if you
can record the stack trace, throwing the exception is useless because that
exception won't go anywhere (that's guaranteed by the spec). Ergo,
destructors are useless during process shutdown. And, because I cannot
control when constructors run, they are useless altogether.

Cheers,

Michi.

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

Nov 17 '05 #6
> What I'm suggesting here is that, for realistic programs, chances are pretty
slim
that a destructor can clean up an unmanaged resource without having to ask for
help from some managed object. But, of course, as soon as that is the case,
the destructor cannot be used anymore. I'm left to conclude that destructors
are so useless that I can't even use them to make assertions about my own
member variables, let alone use them to reclaim unmanaged resources.
cannot make assertions hardly means can't reclaim unmanaged resources. the
framework itself already showed that realistic programs can free unmanaged
resources with finalizers. your illusion is the problem here, really. doesn't
do what you want it to do only means it's useless to you.

It truly would seem appropriate to remove destructors from the language
altogether. At least, that way, people wouldn't have their illusions shattered.
Of course, the alternative would be to not run destructors on process shutdown,
like Java does, and the problem would go away...

Cheers,

Michi.

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

Nov 17 '05 #7

Michi Henning wrote:
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.


Ok -- so how about this:

class someclass
{
~someclass()
{
try
{
System.Diagnostics.Assert(_myMemVar == null);
}
catch(ObjectDisposedException)
{
// you can't do much here anyway
}
}
}

Nov 17 '05 #8
Michi Henning wrote:
"Helge Jensen" <he**********@slog.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.
can record the stack trace, throwing the exception is useless because that
exception won't go anywhere (that's guaranteed by the spec). Ergo,
It goes straight into a message-dialog in debug mode. I put the
stack-trace in the exception-message and that gives a really good place
to start bug-hunting.
destructors are useless during process shutdown. And, because I cannot
control when constructors run, they are useless altogether.


Try rereading my posting, i didn't say they were usefull for semantics,
but for finding bugs. For example the places where Dispose() has been
forgotten.

--
Helge Jensen
mailto:he**********@slog.dk
sip:he**********@slog.dk
-=> Sebastian cover-music: http://ungdomshus.nu <=-
Nov 17 '05 #9
"Dilip" <rd*****@lycos.com> wrote in message
news:11**********************@l41g2000cwc.googlegr oups.com...
Ok -- so how about this:

class someclass
{
~someclass()
{
try
{
System.Diagnostics.Assert(_myMemVar == null);
}
catch(ObjectDisposedException)
{
// you can't do much here anyway
}
}
}


No, don't think so. Under Mono, the code crashes with a NullPointerException
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 #10
"Helge Jensen" <he**********@slog.dk> wrote in message
news:42**************@slog.dk...
Michi Henning wrote:
"Helge Jensen" <he**********@slog.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.HasShutdownStared ) {
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.org> wrote in message
news:%2****************@TK2MSFTNGP14.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.HasShutdownStared ) {
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.org> wrote in message
news:%2****************@TK2MSFTNGP14.phx.gbl...

If that's the behavior you want, how about

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


Unfortunately, I don't see how I can make that thread-safe --
HasShutDownStarted 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.googlegr oups.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(_mem1 == null);
Debug.Assert(_mem2 != null);
Debug.Assert(_mem3 == 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(!processIsGoingAway)
{
Debug.Assert(_mem1 == 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
// !processIsGoingAway has changed and no longer holds.

Debug.Assert(_mem1 != 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
NullPointerException
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****************@TK2MSFTNGP12.phx.gbl... "Dilip" <rd*****@lycos.com> wrote in message
news:11**********************@l41g2000cwc.googlegr oups.com...
Ok -- so how about this:

class someclass
{
~someclass()
{
try
{
System.Diagnostics.Assert(_myMemVar == null);
}
catch(ObjectDisposedException)
{
// you can't do much here anyway
}
}
}


No, don't think so. Under Mono, the code crashes with a
NullPointerException
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
NullPointerException
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

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**********@slog.dk> wrote in message
news:eK*************@TK2MSFTNGP12.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.HasShutdownStarted 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.SuppressFinalize(System.Console) to prevent it
from being finalized. Note that you may need to SuppressFinalize 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********************@l41g2000cwc.googlegrou ps.com...
Admittedly I don't know why this is documented in
Environment.HasShutdownStarted 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.SuppressFinalize(System.Console) to prevent it
from being finalized. Note that you may need to SuppressFinalize 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.WriteLine
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.WriteLine
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.WriteLine("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(_cond1);
Debug.Assert(_cond2);
Debug.Assert(_cond3);
Debug.Assert(_cond4);
Debug.Assert(_cond5);
Debug.Assert(_cond6);
Debug.Assert(_cond7);
}

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
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...
9
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...
6
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...
3
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...
8
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...
3
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
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
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...
6
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 {...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
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...
0
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,...

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.