Garbage collection and callbacks from unmanaged code.

R

R. MacDonald

Hello, all,

I have a .NET application (VB) that passes the address of a delegate to
unmanaged code in a DLL. The unmanaged code then uses the delegate as a
call-back.

This seems to work fine, but now I am worried about garbage collection.
I am concerned that the location of the delegate might be altered as
a result of other (unused) objects being garbage collected. This would
probably cause undesirable results when the unmanaged DLL attempted to
make a subsequent call-back operation.

Although I haven't yet seen such a problem, I understand that garbage
collection is largely uncontrollable and unpredictable. So I am worried
about mysterious and sporadic problems appearing when the application is
actually in use. To avoid this possibility, I tried "pinning" the
delegate, but I get the error message:

"Object contains non-primitive or non-blittable data."

Are my concerns about this justified? If so, what (if anything) can I
do to prevent this problem?

For any who are interested, I have posted relevant code snippets below.

Cheers,
Randy

Private Delegate Function mdgtDataInterface( _
ByVal strAction As String, _
ByVal intActionLen As Integer, _
<MarshalAs(UnmanagedType.SafeArray)> _
ByRef objaData() As Object) As aenmDataRequestStatus

...
...

' DataInterface is the .Net call-back routine.
Dim dgtDataInterface As mdgtDataInterface = AddressOf DataInterface
'''The following statement causes a System.ArgumentException.
'''Dim gchDIPin As GCHandle = GCHandle.Alloc(dgtDataInterface, _
GCHandleType.Pinned)
...

' This calls the unmanaged code and passes the call-back delegate.
Dim intStatus As Integer = RunUnmanaged(intModelAddress, _
dgtDataInterface, _
strMessage)
...
...

' This is the call-back routine
Public Function DataInterface(ByVal strRequest As String, _
ByVal intRequestLength As Integer, _
<MarshalAs(UnmanagedType.SafeArray)> _
ByRef objaData() As Object) _
As aenmDataRequestStatus
...
...
 
M

Mattias Sjögren

Are my concerns about this justified? If so, what (if anything) can I
do to prevent this problem?

As long as you make sure the delegate itself doesn't get garbage
collected, you have nothing to worry about.


Mattias
 
R

R. MacDonald

Hello, Mattias,

Thanks for the reassurance. A reference to the delegate is maintained
for the duration of the time that the unmanaged code is active, so there
is no worry about the delegate itself being garbage collected.

What worries me though, is the following quote from:
http://msdn2.microsoft.com/en-us/library/f144e03t.aspx

<quote>
During a collection, the garbage collector examines the managed heap,
looking for the blocks of address space occupied by unreachable objects.
As it discovers each unreachable object, it uses a memory-copying
function to compact the reachable objects in memory, freeing up the
blocks of address spaces allocated to unreachable objects. Once the
memory for the reachable objects has been compacted, the garbage
collector makes the necessary pointer corrections so that the
application's roots point to the objects in their new locations.
<end-quote>

This sounds to me like the memory location of the delegate is subject to
change. The unmanaged code (Fortran in this case) stores the address of
the delegate in an unmanaged integer variable that is not subject to
adjustment by the garbage collector.

So I have trouble understanding why I shouldn't worry. ;-)

I guess in addition to reassurance, I am also seeking enlightenment.

I can imagine a scheme whereby delegates are treated specially and never
moved as a result of garbage collection. Or I can imagine an
indirection scheme where delegates are always referenced through a fixed
pointer, and it is the address of this pointer that is passed to the
unmanaged code. Incidentally, I seem to have omitted the declaration of
"RunUnmanaged" in my previous post. It it:

Public Function RunUnmanaged(ByVal intFunctionPointer As Integer, _
ByVal dgtCallBackAddr As System.Delegate, _
<MarshalAs(UnmanagedType.BStr)> ByRef strMessage As String) _
As Integer

But these imagined schemes are just idle speculation on my part. The
best reassurance would come from my understanding of how this actually
operates. But, so far, I haven't been able to find any documentation
that makes this clear to me.

Can you point me towards the light?

Cheers,
Randy
 
M

Mattias Sjögren

This sounds to me like the memory location of the delegate is subject to
change. The unmanaged code (Fortran in this case) stores the address of
the delegate in an unmanaged integer variable that is not subject to
adjustment by the garbage collector.

So I have trouble understanding why I shouldn't worry. ;-)

Just as you guessed yourself, the native code doesn't get a direct
pointer to the delegate but rather to some thunk code generated by the
marshaler. This indirection ensures that the native function pointer
remains valid even if the delegate is being moved.


Mattias
 
R

R. MacDonald

Hello, Mattias,

Many thanks for that. My reassurance is now complete.

Do you know of a good reference where this whole area of "marshaling"
(and particularly interacting with unmanaged code) is documented in a
tutorial fashion. I have read the MS reference docs about this, but I
seem to be missing some of the basics and language required to
understand them. :-(

I have one other concern related to SafeArray interactions between
managed and unmanaged code that I will post as a new thread, but I
wouldn't mind being able to learn a little more about the whole area.

Again, thanks for your reply.

Cheers,
Randy
 

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