Hi,
I did a google search and couldn't find something quite like this, so I thought I'd post here.
I've got a mature, high performance native library with lots of optimizations including using zone allocators to increase performance. It's used by a variety of legacy software, and/but I'm building a managed wrapper to expose that functionality to (e.g.) C#. Moving all the code to managed is not an option for a variety of reasons, including performance. I've had to build a circular reference between the native and managed objects:
* the managed class needs a pointer to the native class for business logic; the wrapper does nothing other than call to the native code
* the native code stores native objects in ordered sets, so I retain a reference from the native class back to the managed class for iterators, etc. - I don't want to duplicate list and set management
Example illustrative code:
ref class Managed_eh {
class Native_eh *n_this;
};
class Native_eh {
Native_eh *prev, *next;
msclr::auto_gcroot<managed_eh> ^gc_this;
};</managed_eh>
* If client code creates the managed object, then the native object is necessarily created.
* If the native object is created due to internal logic, then we don't create the managed wrapper until we need to - to avoid unnecessary allocations, etc., otherwise the benefits of the zone allocators, etc. are lost. Also, there are many times when the managed wrapper is never actually needed.
*
gc_this
must not be cleared (to maintain consistency) so long as there's client managed code with a handle to the
Managed_eh
object.
Clean-up is the concern. Neither the wrapper class, nor the native class know when they are no longer interesting to client code and clean up. Sometimes I can solve this problem by cleaning up the container objects managing the sets of
Native_eh
. But it's not a generic solution.
* In the strictly native world, this would be a trivial fix by changing the hierarchy.
* In the COM reference-counter world, this is equally easy with a different solution:
— if
gc_this
is not set, then clean up when the reference counter to Managed_eh reaches 0
— if
gc_this
is set, then when the reference counter on the managed pointer drops to 1, we know we can clean up because we know exactly what that '1' is (
gc_this
).
What is the appropriate ".Net" approach to solving this problem?
Thanks,
—Rob
[EDIT: in response to Answers]
Interesting discussion, thanks, though I don't think it helps my particular problem.
I'm not particularly concerned on precisely
when the clean-up of the garbage-collected objects occur, I can easily work around that. I'm more concerned that they
can.
* So long as the native object retains the handle to the managed wrapper, then the garbage collector will not perform clean-up. Tests show this, and it makes sense because the garbage collector cannot track references in the native side.
* But...the native object cannot release its handle until it knows that nobody else on the managed side has a handle - or risk invalidating consistency.
Said another way, if the native object has the only reference to the managed wrapper, than we (it) can perform clean-up. But how can the native object determine that nobody else is interested in its wrapper?
In the COM world, we could examine reference counters to conclude "
I'm the only one left with a handle to this object so I can release it and we can clean up". But I don't see that available in the .Net world.
Is there a way to (efficiently) query the dependency graph used by the garbage collector?
Is there an event that tells you when the state of the dependency graph for a given object (the managed wrapper) changes?
—Rob