Skip to content

Commit fc350c1

Browse files
github-actions[bot]jkotasAaronRobinsonMSFTjeffschwMSFT
authored
[release/9.0-staging] Move ComWrappers AddRef to C/C++ (#110815)
* Move ComWrappers AddRef to C/C++ Xaml invokes AddRef while holding a lock that it *also* holds while a GC is in progress. Managed AddRef had to synchronize with the GC that caused intermittent deadlocks with the other thread holding Xaml's lock. This change reverts the managed AddRef implementation to match .NET Native and CoreCLR. Fixes #110747 * Apply suggestions from code review Co-authored-by: Aaron Robinson <[email protected]> * Update src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp * Update src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp * Build break * Update src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp * Apply suggestions from code review * Update src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs --------- Co-authored-by: Jan Kotas <[email protected]> Co-authored-by: Aaron Robinson <[email protected]> Co-authored-by: Jeff Schwartz <[email protected]>
1 parent f8df617 commit fc350c1

File tree

5 files changed

+85
-10
lines changed

5 files changed

+85
-10
lines changed

src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,57 @@ FCIMPL2(void, RhUnregisterRefCountedHandleCallback, void * pCallout, MethodTable
7070
RestrictedCallouts::UnregisterRefCountedHandleCallback(pCallout, pTypeFilter);
7171
}
7272
FCIMPLEND
73+
74+
// This structure mirrors the managed type System.Runtime.InteropServices.ComWrappers.ManagedObjectWrapper.
75+
struct ManagedObjectWrapper
76+
{
77+
intptr_t HolderHandle;
78+
uint64_t RefCount;
79+
80+
int32_t UserDefinedCount;
81+
void* /* ComInterfaceEntry */ UserDefined;
82+
void* /* InternalComInterfaceDispatch* */ Dispatches;
83+
84+
int32_t /* CreateComInterfaceFlagsEx */ Flags;
85+
86+
uint32_t AddRef()
87+
{
88+
return GetComCount((uint64_t)PalInterlockedIncrement64((int64_t*)&RefCount));
89+
}
90+
91+
static const uint64_t ComRefCountMask = 0x000000007fffffffUL;
92+
93+
static uint32_t GetComCount(uint64_t c)
94+
{
95+
return (uint32_t)(c & ComRefCountMask);
96+
}
97+
};
98+
99+
// This structure mirrors the managed type System.Runtime.InteropServices.ComWrappers.InternalComInterfaceDispatch.
100+
struct InternalComInterfaceDispatch
101+
{
102+
void* Vtable;
103+
ManagedObjectWrapper* _thisPtr;
104+
};
105+
106+
static ManagedObjectWrapper* ToManagedObjectWrapper(void* dispatchPtr)
107+
{
108+
return ((InternalComInterfaceDispatch*)dispatchPtr)->_thisPtr;
109+
}
110+
111+
//
112+
// AddRef is implemented in native code so that it does not need to synchronize with the GC. This is important because Xaml
113+
// invokes AddRef while holding a lock that it *also* holds while a GC is in progress. If AddRef was managed, we would have
114+
// to synchronize with the GC before entering AddRef, which would deadlock with the other thread holding Xaml's lock.
115+
//
116+
static uint32_t __stdcall IUnknown_AddRef(void* pComThis)
117+
{
118+
ManagedObjectWrapper* wrapper = ToManagedObjectWrapper(pComThis);
119+
return wrapper->AddRef();
120+
}
121+
122+
FCIMPL0(void*, RhGetIUnknownAddRef)
123+
{
124+
return (void*)&IUnknown_AddRef;
125+
}
126+
FCIMPLEND

src/coreclr/nativeaot/Runtime/unix/PalRedhawkInline.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ FORCEINLINE int32_t PalInterlockedIncrement(_Inout_ int32_t volatile *pDst)
3030
return result;
3131
}
3232

33+
FORCEINLINE int64_t PalInterlockedIncrement64(_Inout_ int64_t volatile *pDst)
34+
{
35+
int64_t result = __sync_add_and_fetch(pDst, 1);
36+
PalInterlockedOperationBarrier();
37+
return result;
38+
}
39+
3340
FORCEINLINE int32_t PalInterlockedDecrement(_Inout_ int32_t volatile *pDst)
3441
{
3542
int32_t result = __sync_sub_and_fetch(pDst, 1);

src/coreclr/nativeaot/Runtime/windows/PalRedhawkInline.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,31 @@ FORCEINLINE int64_t PalInterlockedExchange64(_Inout_ int64_t volatile *pDst, int
6767
iOldValue) != iOldValue);
6868
return iOldValue;
6969
}
70+
71+
FORCEINLINE int64_t PalInterlockedIncrement64(_Inout_ int64_t volatile *Addend)
72+
{
73+
int64_t Old;
74+
do {
75+
Old = *Addend;
76+
} while (PalInterlockedCompareExchange64(Addend,
77+
Old + 1,
78+
Old) != Old);
79+
return Old + 1;
80+
}
7081
#else // HOST_X86
7182
EXTERN_C int64_t _InterlockedExchange64(int64_t volatile *, int64_t);
7283
#pragma intrinsic(_InterlockedExchange64)
7384
FORCEINLINE int64_t PalInterlockedExchange64(_Inout_ int64_t volatile *pDst, int64_t iValue)
7485
{
7586
return _InterlockedExchange64(pDst, iValue);
7687
}
88+
89+
EXTERN_C int64_t _InterlockedIncrement64(int64_t volatile *);
90+
#pragma intrinsic(_InterlockedIncrement64)
91+
FORCEINLINE int64_t PalInterlockedIncrement64(_Inout_ int64_t volatile *pDst)
92+
{
93+
return _InterlockedIncrement64(pDst);
94+
}
7795
#endif // HOST_X86
7896

7997
#if defined(HOST_AMD64) || defined(HOST_ARM64)

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,7 +1250,7 @@ public static void RegisterForMarshalling(ComWrappers instance)
12501250
public static unsafe void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease)
12511251
{
12521252
fpQueryInterface = (IntPtr)(delegate* unmanaged<IntPtr, Guid*, IntPtr*, int>)&ComWrappers.IUnknown_QueryInterface;
1253-
fpAddRef = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IUnknown_AddRef;
1253+
fpAddRef = RuntimeImports.RhGetIUnknownAddRef(); // Implemented in C/C++ to avoid GC transitions
12541254
fpRelease = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IUnknown_Release;
12551255
}
12561256

@@ -1395,13 +1395,6 @@ internal static unsafe int IUnknown_QueryInterface(IntPtr pThis, Guid* guid, Int
13951395
return wrapper->QueryInterface(in *guid, out *ppObject);
13961396
}
13971397

1398-
[UnmanagedCallersOnly]
1399-
internal static unsafe uint IUnknown_AddRef(IntPtr pThis)
1400-
{
1401-
ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis);
1402-
return wrapper->AddRef();
1403-
}
1404-
14051398
[UnmanagedCallersOnly]
14061399
internal static unsafe uint IUnknown_Release(IntPtr pThis)
14071400
{
@@ -1474,8 +1467,7 @@ private static unsafe IntPtr CreateDefaultIReferenceTrackerTargetVftbl()
14741467
{
14751468
IntPtr* vftbl = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ComWrappers), 7 * sizeof(IntPtr));
14761469
vftbl[0] = (IntPtr)(delegate* unmanaged<IntPtr, Guid*, IntPtr*, int>)&ComWrappers.IReferenceTrackerTarget_QueryInterface;
1477-
vftbl[1] = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IUnknown_AddRef;
1478-
vftbl[2] = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IUnknown_Release;
1470+
GetIUnknownImpl(out _, out vftbl[1], out vftbl[2]);
14791471
vftbl[3] = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IReferenceTrackerTarget_AddRefFromReferenceTracker;
14801472
vftbl[4] = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IReferenceTrackerTarget_ReleaseFromReferenceTracker;
14811473
vftbl[5] = (IntPtr)(delegate* unmanaged<IntPtr, uint>)&ComWrappers.IReferenceTrackerTarget_Peg;

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@ internal enum GcRestrictedCalloutKind
511511
[RuntimeImport(RuntimeLibrary, "RhUnregisterRefCountedHandleCallback")]
512512
internal static extern unsafe void RhUnregisterRefCountedHandleCallback(IntPtr pCalloutMethod, MethodTable* pTypeFilter);
513513

514+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
515+
[RuntimeImport(RuntimeLibrary, "RhGetIUnknownAddRef")]
516+
internal static extern IntPtr RhGetIUnknownAddRef();
517+
514518
#if FEATURE_OBJCMARSHAL
515519
[MethodImplAttribute(MethodImplOptions.InternalCall)]
516520
[RuntimeImport(RuntimeLibrary, "RhRegisterObjectiveCMarshalBeginEndCallback")]

0 commit comments

Comments
 (0)