Destructors are useless?

M

Michi Henning

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.
 
M

Michi Henning

Helge Jensen said:
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.
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.
 
D

Dilip

Michi said:
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/d...emenvironmentclasshasshutdownstartedtopic.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
 
H

Helge Jensen

Michi said:
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...
 
M

Michi Henning

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.
 
D

Dilip

Helge said:
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
 
M

Michi Henning

Dilip said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top