On 7/18/07, I wrote :
> Looks like the _wapi_private_handles array expands indefinitely but
> never shrinks.

Hmm, by "shrinks" I meant the allocated slots are not reused like they
should because of the Close() I guess.

I've continued my investigation from the low-level handles.c to the managed.

If you trace the testcase you'll get this.

. . . . . ENTER: System.Runtime.InteropServices.SafeHandle:Dispose
(bool)(this:0x82953c0[Microsoft.Win32.SafeHandles.SafeWaitHandle
leak-bad.exe], 1, )
. . . . . . ENTER: System.Runtime.InteropServices.SafeHandle:Close
()(this:0x82953c0[Microsoft.Win32.SafeHandles.SafeWaitHandle
leak-bad.exe], )
. . . . . . . ENTER: (wrapper managed-to-native)
System.Threading.Interlocked:CompareExchange
(int&,int,int)([BYREF:0x82953d0], 0, 1, )
. . . . . . . LEAVE: (wrapper managed-to-native)
System.Threading.Interlocked:CompareExchange (int&,int,int)result=1
**************** Here we should actually call ReleaseHandle() !
. . . . . . LEAVE: System.Runtime.InteropServices.SafeHandle:Close ()
. . . . . LEAVE: System.Runtime.InteropServices.SafeHandle:Dispose (bool)

ReleaseHandle does not get called because the handle is wrapped into
SafeHandle with ownsHandle = false.

This leaky behavior was actually "forecasted" in a visionnary comment
in System.Threading/WaitHandle.cs 2.0 region :

// Notice, from the 2.x documentation:
//    Assigning a new value to the Handle property, will not release
//    the previous handle, this could lead to a leak
safe_wait_handle = new SafeWaitHandle (value, false);

Replace false by true, problem solved?
Nope, because then ReleaseHandle() from SafeWaitHandle throws an
ObjectDisposed exception because concrete WaitHandle.ReleaseHandle()
call the native Close_event with GetDangerousHandle() as argument,
though the refcount has been set to 0 earlier in Dispose().
I'm not sure it would be wise to change the real refcount field in
Close() after the ReleaseHandle() either because of potential race
conditions (?), so in GetDangerousHandle() another way is to check
moreover the refcount if the handle is invalid as set by Close() after
the handle release anyway.

Attached tentative patch fixes the memory leak :)
Index: class/corlib/System.Threading/WaitHandle.cs
===================================================================
--- class/corlib/System.Threading/WaitHandle.cs	(révision 82236)
+++ class/corlib/System.Threading/WaitHandle.cs	(copie de travail)
@@ -221,12 +221,7 @@
 			[SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
 			[SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
 			set {
-				//
-				// Notice, from the 2.x documentation:
-				//    Assigning a new value to the Handle property, will not release
-				//    the previous handle, this could lead to a leak
-				//
-				safe_wait_handle = new SafeWaitHandle (value, false);
+				safe_wait_handle = new SafeWaitHandle (value, true);
 			}
 		}
 		
Index: class/corlib/System.Runtime.InteropServices/SafeHandle.cs
===================================================================
--- class/corlib/System.Runtime.InteropServices/SafeHandle.cs	(révision 82236)
+++ class/corlib/System.Runtime.InteropServices/SafeHandle.cs	(copie de travail)
@@ -124,7 +124,7 @@
 		[ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
 		public IntPtr DangerousGetHandle ()
 		{
-			if (refcount == 0){
+			if (refcount == 0 && handle == invalid_handle_value) {
 				throw new ObjectDisposedException (GetType ().FullName);
 			}
 
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to