Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

gcroot<> objects and finalization

4 views
Skip to first unread message

Kevin Frey

unread,
Oct 11, 2009, 7:07:46 PM10/11/09
to
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


Jochen Kalmbach [MVP]

unread,
Oct 12, 2009, 1:33:48 AM10/12/09
to
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/

Ben Voigt [C++ MVP]

unread,
Oct 12, 2009, 2:05:59 AM10/12/09
to

"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message
news:#NljU2vS...@TK2MSFTNGP02.phx.gbl...


> 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!

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.

>
>> 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)

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

Jochen Kalmbach [MVP]

unread,
Oct 12, 2009, 2:18:30 AM10/12/09
to
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...

Ben Voigt [C++ MVP]

unread,
Oct 12, 2009, 12:06:34 PM10/12/09
to

"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message

news:#JXmSPwS...@TK2MSFTNGP02.phx.gbl...


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

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.

Ben Voigt [C++ MVP]

unread,
Oct 12, 2009, 12:09:37 PM10/12/09
to

"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message

news:#JXmSPwS...@TK2MSFTNGP02.phx.gbl...


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

oops, I see the OP is specifically talking about appdomain shutdown, when
the GC starts killing off root-reachable objects.

There's a mechanism specifically for dealing with this:
http://msdn.microsoft.com/en-us/library/system.environment.hasshutdownstarted.aspx

Kevin Frey

unread,
Oct 13, 2009, 6:06:25 PM10/13/09
to
> 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


0 new messages