gcroot<> objects and finalization

K

Kevin Frey

Hello All,

I have the following scenario:

Class Managed (C++/CLI) holds a pointer to class Unmanaged (native C++).
Class Unmanaged in turn holds a gcroot< > pointer (to a TcpClient).

On process exit, the finalizer of "Managed" deletes the Unmanaged object.
The destructor of Unmanaged tries to use the gcroot<> object (to perform a
disconnect) and I seem to get an Exception that the gcroot object has
already been disposed (ObjectDisposedException).

So the question really comes down to what assurances exist at process exit
in terms of managed objects held by the gcroot<> template, and whether they
can be assumed to still exist. Or, does the fact that I have a finalizer
indirectly referencing another managed object (ie. via the native object)
mean I am violating the rule that a finalizer should not refer to other
managed objects (which is a definite no-no in a fully managed environment)

Thanks

Kevin
 
J

Jochen Kalmbach [MVP]

Hi Kevin!
Class Managed (C++/CLI) holds a pointer to class Unmanaged (native C++).
Class Unmanaged in turn holds a gcroot< > pointer (to a TcpClient).

On process exit, the finalizer of "Managed" deletes the Unmanaged object.

This is ok.
The destructor of Unmanaged tries to use the gcroot<> object (to perform a
disconnect) and I seem to get an Exception that the gcroot object has
already been disposed (ObjectDisposedException).

If you are called from the finalizer, you must never use any .NET
objects! This is not allowed!

So I suggest you implement a "Dispose" Methode in your unmanaged class.
And I also suggest, you should implement the IDisposable-Pattern in your
managed class.
If the managed class is called from Dispose, then you should also
Dispose other managed classes and delete the unmanaged class.

If you are called from the finalizer, you should *only* delete the
unmanaged class and never ever use or reference managed classes!
So the question really comes down to what assurances exist at process exit
in terms of managed objects held by the gcroot<> template, and whether they
can be assumed to still exist.

Inside a finilizer, you must assume, that any other managed objects does
not exist anymore!
Or, does the fact that I have a finalizer
indirectly referencing another managed object (ie. via the native object)
mean I am violating the rule that a finalizer should not refer to other
managed objects (which is a definite no-no in a fully managed environment)

Exactly.

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
 
B

Ben Voigt [C++ MVP]

Jochen Kalmbach said:
Hi Kevin!


This is ok.


If you are called from the finalizer, you must never use any .NET objects!
This is not allowed!

So I suggest you implement a "Dispose" Methode in your unmanaged class.
And I also suggest, you should implement the IDisposable-Pattern in your
managed class.
If the managed class is called from Dispose, then you should also Dispose
other managed classes and delete the unmanaged class.

If you are called from the finalizer, you should *only* delete the
unmanaged class and never ever use or reference managed classes!


Inside a finilizer, you must assume, that any other managed objects does
not exist anymore!

This is because when multiple objects become unreachable during a single GC
cycle, there's no guarantee on which order they will be finalized.

The object in the gcroot is reachable though. Reachability analysis can't
look into the managed class.

The only time reachable objects can be finalized is during appdomain
shutdown, but I think finalization is actually just skipped in that case.

I suspect the TcpClient isn't being finalized at all, but explicitly
disposed in two different places.

That's a misstatement of the rule. A finalizer should not refer to any
managed objects which aren't independently reachable from a root, because
they could already have been collected. Objects protected against
collection using a static field or C++ gcroot template can be used during
finalization.



__________ Information from ESET NOD32 Antivirus, version of virus signature database 4498 (20091011) __________

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com
 
J

Jochen Kalmbach [MVP]

Hi Ben!
That's a misstatement of the rule. A finalizer should not refer to any
managed objects which aren't independently reachable from a root,
because they could already have been collected. Objects protected
against collection using a static field or C++ gcroot template can be
used during finalization.

Yes, it is possible to use "reachable" managed objects... but this is
bad app-design and could lead to object-resurrection...

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
 
B

Ben Voigt [C++ MVP]

Jochen Kalmbach said:
Hi Ben!


Yes, it is possible to use "reachable" managed objects... but this is bad
app-design and could lead to object-resurrection...

There's a distinction between root-reachable vs addressable from the
finalizer. Root-reachable objects cannot have been finalized, so no
problems with resurrection. But a finalizer also has access to the fields
of the object being finalized, some of these may be the sole surviving
reference to an object that is not root-reachable, and might already be
gone.

Anyway, for the OP's problem, the gcroot keeps the TcpClient alive, so the
ObjectDisposedException was not caused by the GC running the TcpClient's
finalizer. Something else is disposing the TcpClient.
 
K

Kevin Frey

This is because when multiple objects become unreachable during a single
GC cycle, there's no guarantee on which order they will be finalized.
The object in the gcroot is reachable though. Reachability analysis can't
look into the managed class.

The only time reachable objects can be finalized is during appdomain
shutdown, but I think finalization is actually just skipped in that case.

I suspect the TcpClient isn't being finalized at all, but explicitly
disposed in two different places.

I have since found various articles on the web that actually describes the
finalization process, which in particular comments that during AppDomain
shutdown all reachable finalizable objects are also finalized. This would
seem to explain my situation.

The TcpClient being disposed in two places shouldn't actually be a problem,
since Dispose( ) is canonically intended to handle that situation - but I'm
pretty sure that does not happen in my case anyway.

Fortunately there are a couple of means to test if you are in an AppDomain
shutdown and therefore I can code against it.

Kevin
 

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