diff options
-rw-r--r-- | mono/metadata/cominterop.c | 53 | ||||
-rw-r--r-- | mono/tests/cominterop.cs | 52 |
2 files changed, 85 insertions, 20 deletions
diff --git a/mono/metadata/cominterop.c b/mono/metadata/cominterop.c index 9ec1d0f10c3..d0e2241063c 100644 --- a/mono/metadata/cominterop.c +++ b/mono/metadata/cominterop.c @@ -173,6 +173,31 @@ typedef struct { MonoCCW* ccw; } MonoCCWInterface; +/* + * COM Callable Wrappers + * + * CCWs may be called on threads that aren't attached to the runtime, they can + * then run managed code or the method implementations may use coop handles. + * Use the macros below to setup the thread state. + * + * For managed methods, the runtime marshaling wrappers handle attaching and + * coop state switching. + */ + +#define MONO_CCW_CALL_ENTER do { \ + gpointer dummy; \ + gpointer orig_domain = mono_threads_attach_coop (mono_domain_get (), &dummy); \ + MONO_ENTER_GC_UNSAFE; \ + HANDLE_FUNCTION_ENTER (); \ + do {} while (0) + +#define MONO_CCW_CALL_EXIT \ + HANDLE_FUNCTION_RETURN (); \ + MONO_EXIT_GC_UNSAFE; \ + mono_threads_detach_coop (orig_domain, &dummy); \ + } while (0) + + /* IUnknown */ static int STDCALL cominterop_ccw_addref (MonoCCWInterface* ccwe); @@ -2568,12 +2593,9 @@ static int STDCALL cominterop_ccw_addref (MonoCCWInterface* ccwe) { int result; - gpointer dummy; - gpointer orig_domain = mono_threads_attach_coop (mono_domain_get (), &dummy); - MONO_ENTER_GC_UNSAFE; + MONO_CCW_CALL_ENTER; result = cominterop_ccw_addref_impl (ccwe); - MONO_EXIT_GC_UNSAFE; - mono_threads_detach_coop (orig_domain, &dummy); + MONO_CCW_CALL_EXIT; return result; } @@ -2602,12 +2624,9 @@ static int STDCALL cominterop_ccw_release (MonoCCWInterface* ccwe) { int result; - gpointer dummy; - gpointer orig_domain = mono_threads_attach_coop (mono_domain_get (), &dummy); - MONO_ENTER_GC_UNSAFE; + MONO_CCW_CALL_ENTER; result = cominterop_ccw_release_impl (ccwe); - MONO_EXIT_GC_UNSAFE; - mono_threads_detach_coop (orig_domain, &dummy); + MONO_CCW_CALL_EXIT; return result; } @@ -2654,12 +2673,9 @@ static int STDCALL cominterop_ccw_queryinterface (MonoCCWInterface* ccwe, const guint8* riid, gpointer* ppv) { int result; - gpointer dummy; - gpointer orig_domain = mono_threads_attach_coop (mono_domain_get (), &dummy); - MONO_ENTER_GC_UNSAFE; + MONO_CCW_CALL_ENTER; result = cominterop_ccw_queryinterface_impl (ccwe, riid, ppv); - MONO_EXIT_GC_UNSAFE; - mono_threads_detach_coop (orig_domain, &dummy); + MONO_CCW_CALL_EXIT; return result; } @@ -2777,12 +2793,9 @@ cominterop_ccw_get_ids_of_names (MonoCCWInterface* ccwe, gpointer riid, guint32 lcid, gint32 *rgDispId) { int result; - gpointer dummy; - gpointer orig_domain = mono_threads_attach_coop (mono_domain_get(), &dummy); - MONO_ENTER_GC_UNSAFE; + MONO_CCW_CALL_ENTER; result = cominterop_ccw_get_ids_of_names_impl (ccwe, riid, rgszNames, cNames, lcid, rgDispId); - MONO_EXIT_GC_UNSAFE; - mono_threads_detach_coop (orig_domain, &dummy); + MONO_CCW_CALL_EXIT; return result; } diff --git a/mono/tests/cominterop.cs b/mono/tests/cominterop.cs index 11cb68bcdbc..3ff018efa71 100644 --- a/mono/tests/cominterop.cs +++ b/mono/tests/cominterop.cs @@ -659,6 +659,9 @@ public class Tests } } + if (CallableWrapperLeakTest () != 0) + return 209; + #endregion // COM Callable Wrapper Tests #region SAFEARRAY tests @@ -1630,6 +1633,55 @@ public class Tests return 2; return 0; } + + // Doesn't matter what this is + internal class CallableWrapperLeakTestClass { + } + + public static int CallableWrapperLeakTest () { + bool ok = true; + GCHandle h = HideFromGC (() => { + var o = new CallableWrapperLeakTestClass(); + var weakHandle = GCHandle.Alloc (o, GCHandleType.Weak); + var pUnk = Marshal.GetIUnknownForObject (o); + int c = Marshal.Release(pUnk); + o = null; + if (c != 0) { + Console.Error.WriteLine ("Expected IUnknown refcount on a CCW to be 0 after Release, was {0}", c); + ok = false; + } + return weakHandle; + }); + if (!ok) + return 1; + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + HideFromGC(() => { + var o = h.Target; + if (o != null) { + Console.Error.WriteLine ("Expected weak handle to be null after GC, but the object (of type {0}) was retained", o.GetType()); + ok = false; + } + return "done"; // doesn't matter + }); + if (!ok) + return 2; + return 0; + } + + [MethodImpl (MethodImplOptions.NoInlining)] + private static T HideFromGC<T> (Func<T> f) => HideFromGC (20, f); + + [MethodImpl (MethodImplOptions.NoInlining)] + private static T HideFromGC<T> (int rec, Func<T> f) { + if (rec <= 0) + return f (); + else + return HideFromGC (rec - 1, f); + } + } public class TestVisible |