[Mono-list] Problem with Dispose() and unmanaged resources

Edd Dumbill edd@usefulinc.com
Tue, 04 Jan 2005 16:42:44 +0000


Hi,

In Redland# we use the pattern described by Microsoft for using
IDisposable to wrap unmanaged resources, described here:
http://makeashorterlink.com/?Y3861282A

I have a private member of type IntPtr set to the unmanaged resource,
and a P/Invoke to a cleanup C function is made in the Dispose call, and
then the member set to IntPtr.Zero, as described under "Implementing a
Dispose method".

The problem is that -- when Dispose is run from the Finalizer (that is,
Dispose wasn't called before the object was GC'd) -- it seems the IntPtr
member has already been finalized, and there's a NullReferenceException
generated.  Under Mono 1.0.4 this prints out:

Unhandled Exception: System.NullReferenceException: Object reference not
set to an instance of an object

And then the runtime hangs.  This happens only occasionally, and less
frequently if I'm generating trace output.

Many of the traced sessions look a bit like this:

ENTER: (wrapper runtime-invoke) System.Object:runtime_invoke_void
(object,intptr,intptr,intptr)([Redland.Serializer:0x8096a90], (nil),
0xb6a659c8, 0xb619e2a0, ). ENTER: Redland.Serializer:Finalize
()(this:0x8096a90[Redland.Serializer test.exe], )
. . ENTER: Redland.Serializer:Dispose
(bool)(this:0x8096a90[Redland.Serializer test.exe], 0, )
. . . ENTER: (wrapper managed-to-native)
Redland.Serializer:librdf_free_serializer (intptr)(0x8172ba8, )
. . . . ENTER: (wrapper runtime-invoke)
System.Object:runtime_invoke_void
(object,intptr,intptr,intptr)([System.NullReferenceException:0x8098c90],
(nil), (nil), 0xb619e3b0, )
. . . . . ENTER: System.NullReferenceException:.ctor
()(this:0x8098c90[System.NullReferenceException test.exe], )
. . . . . . ENTER: Locale:GetText (string)([STRING:0x8111c78:A null
value was found where an object instance was required.], )
. . . . . . LEAVE: Locale:GetText (string)[STRING:0x8111c78:A null value
was found where an object instance was required.]
. . . . . LEAVE: System.NullReferenceException:.ctor ()
. . . . LEAVE: (wrapper runtime-invoke)
System.Object:runtime_invoke_void
(object,intptr,intptr,intptr)[OBJECT:(nil)]
EXCEPTION handling: NullReferenceException
EXCEPTION: finally clause 0 of Redland.Serializer:Finalize ()
. . . . ENTER: System.Object:Finalize
()(this:0x8096a90[Redland.Serializer test.exe], )
. . . . LEAVE: System.Object:Finalize ()
EXCEPTION: catch found at clause 0 of (wrapper runtime-invoke)
System.Object:runtime_invoke_void (object,intptr,intptr,intptr)

What I see happening above is the NullReferenceException being thrown
when it attempts to pass the value of the private IntPtr to the C
cleanup function, librdf_free_serializer().

I pulled Mono SVN HEAD to see if things worked any different there.  The
good news is that we no longer seem to get the random hangs that 1.0.4
generates, but the trace shows that the NullReferenceException thing is
still happening.  At the best, this means memory leaks are going to
happen.

Have I got something wrong here, or is there a bug with finalizing
classes?  The code is structured just the way the MSDN example suggests.

-- Edd