Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,16 @@ public partial struct DependentHandle : IDisposable
//
// This struct intentionally does no self-synchronization. It's up to the caller to
// to use DependentHandles in a thread-safe way.
//
// There are two types of dependant handles: "normal" and "defer finalize". The "defer
// finalize" version promotes the secondary until the primary has been completely collected.
// The low order bit of the handle is stolen to indicate that the handle is the "defer finalize"
// flavor. It should be masked off before using.
// =========================================================================================

private IntPtr _handle;
private const nint IsDeferFinalizeBit = 1;

private nint _taggedHandle;

/// <summary>
/// Initializes a new instance of the <see cref="DependentHandle"/> struct with the specified arguments.
Expand All @@ -63,18 +70,33 @@ public partial struct DependentHandle : IDisposable
/// <param name="dependent">The dependent object instance to associate with <paramref name="target"/>.</param>
public DependentHandle(object? target, object? dependent)
{
IntPtr handle = InternalAlloc(target, dependent);
IntPtr handle = InternalAlloc(false, target, dependent);
if (handle == 0)
handle = InternalAllocWithGCTransition(false, target, dependent);
_taggedHandle = handle;
}

/// <summary>
/// Initializes a new instance of the <see cref="DependentHandle"/> struct with the specified arguments.
/// </summary>
/// <param name="isDeferFinalize">If true, the dependent is not finalized until the target is complexly collected.</param>
/// <param name="target">The target object instance to track.</param>
/// <param name="dependent">The dependent object instance to associate with <paramref name="target"/>.</param>
internal DependentHandle(bool isDeferFinalize, object? target, object? dependent)
{
// no need to check for null result: InternalInitialize expected to throw OOM.
IntPtr handle = InternalAlloc(isDeferFinalize, target, dependent);
if (handle == 0)
handle = InternalAllocWithGCTransition(target, dependent);
_handle = handle;
handle = InternalAllocWithGCTransition(isDeferFinalize, target, dependent);
_taggedHandle = handle | (isDeferFinalize ? IsDeferFinalizeBit : 0);
}

/// <summary>
/// Gets a value indicating whether this instance was constructed with
/// <see cref="DependentHandle(object?, object?)"/> and has not yet been disposed.
/// </summary>
/// <remarks>This property is thread-safe.</remarks>
public readonly bool IsAllocated => _handle != 0;
public readonly bool IsAllocated => _taggedHandle != 0;

/// <summary>
/// Gets or sets the target object instance for the current handle. The target can only be set to a <see langword="null"/> value
Expand All @@ -88,7 +110,7 @@ public object? Target
{
readonly get
{
IntPtr handle = _handle;
nint handle = _taggedHandle & ~IsDeferFinalizeBit;

if (handle == 0)
{
Expand All @@ -99,7 +121,7 @@ readonly get
}
set
{
IntPtr handle = _handle;
nint handle = _taggedHandle & ~IsDeferFinalizeBit;

if (handle == 0 || value is not null)
{
Expand All @@ -125,7 +147,7 @@ public object? Dependent
{
readonly get
{
IntPtr handle = _handle;
nint handle = _taggedHandle & ~IsDeferFinalizeBit;

if (handle == 0)
{
Expand All @@ -136,14 +158,16 @@ readonly get
}
set
{
IntPtr handle = _handle;
nint taggedHandle = _taggedHandle;

if (handle == 0)
if (taggedHandle == 0)
{
ThrowHelper.ThrowInvalidOperationException();
}

InternalSetDependent(handle, value);
nint handle = taggedHandle & ~IsDeferFinalizeBit;
bool isDeferFinalize = (taggedHandle & IsDeferFinalizeBit) != 0;
InternalSetDependent(isDeferFinalize, handle, value);
}
}

Expand All @@ -161,7 +185,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent
{
get
{
IntPtr handle = _handle;
nint handle = _taggedHandle & ~IsDeferFinalizeBit;

if (handle == 0)
{
Expand All @@ -181,7 +205,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent
/// <remarks>This method mirrors <see cref="Target"/>, but without the allocation check.</remarks>
internal readonly object? UnsafeGetTarget()
{
return InternalGetTarget(_handle);
return InternalGetTarget(_taggedHandle & ~IsDeferFinalizeBit);
}

/// <summary>
Expand All @@ -196,7 +220,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent
/// </remarks>
internal readonly object? UnsafeGetTargetAndDependent(out object? dependent)
{
return InternalGetTargetAndDependent(_handle, out dependent);
return InternalGetTargetAndDependent(_taggedHandle & ~IsDeferFinalizeBit, out dependent);
}

/// <summary>
Expand All @@ -205,7 +229,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent
/// <remarks>This method mirrors the <see cref="Target"/> setter, but without allocation and input checks.</remarks>
internal readonly void UnsafeSetTargetToNull()
{
InternalSetTargetToNull(_handle);
InternalSetTargetToNull(_taggedHandle & ~IsDeferFinalizeBit);
}

/// <summary>
Expand All @@ -214,7 +238,9 @@ internal readonly void UnsafeSetTargetToNull()
/// <remarks>This method mirrors <see cref="Dependent"/>, but without the allocation check.</remarks>
internal readonly void UnsafeSetDependent(object? dependent)
{
InternalSetDependent(_handle, dependent);
nint handle = _taggedHandle & ~IsDeferFinalizeBit;
bool isDeferFinalize = (_taggedHandle & IsDeferFinalizeBit) != 0;
InternalSetDependent(isDeferFinalize, handle, dependent);
}

/// <inheritdoc cref="IDisposable.Dispose"/>
Expand All @@ -223,61 +249,63 @@ public void Dispose()
{
// Forces the DependentHandle back to non-allocated state
// (if not already there) and frees the handle if needed.
IntPtr handle = _handle;
nint taggedHandle = _taggedHandle;

if (handle != 0)
if (taggedHandle != 0)
{
_handle = 0;
_taggedHandle = 0;

if (!InternalFree(handle))
nint handle = taggedHandle & ~IsDeferFinalizeBit;
bool isDeferFinalize = (taggedHandle & IsDeferFinalizeBit) != 0;
if (!InternalFree(isDeferFinalize, handle))
{
InternalFreeWithGCTransition(handle);
InternalFreeWithGCTransition(isDeferFinalize, handle);
}
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr InternalAlloc(object? target, object? dependent);
private static extern IntPtr InternalAlloc(bool isDeferFinalize, object? target, object? dependent);

[MethodImpl(MethodImplOptions.NoInlining)]
private static IntPtr InternalAllocWithGCTransition(object? target, object? dependent)
=> _InternalAllocWithGCTransition(ObjectHandleOnStack.Create(ref target), ObjectHandleOnStack.Create(ref dependent));
private static IntPtr InternalAllocWithGCTransition(bool isDeferFinalize, object? target, object? dependent)
=> _InternalAllocWithGCTransition(isDeferFinalize, ObjectHandleOnStack.Create(ref target), ObjectHandleOnStack.Create(ref dependent));

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DependentHandle_InternalAllocWithGCTransition")]
private static partial IntPtr _InternalAllocWithGCTransition(ObjectHandleOnStack target, ObjectHandleOnStack dependent);
private static partial IntPtr _InternalAllocWithGCTransition([MarshalAs(UnmanagedType.Bool)] bool isDeferFinalize, ObjectHandleOnStack target, ObjectHandleOnStack dependent);

#if DEBUG
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? InternalGetTarget(IntPtr dependentHandle);
private static extern object? InternalGetTarget(nint dependentHandle);
#else
// This optimization is the same that is used in GCHandle in RELEASE mode.
// This is not used in DEBUG builds as the runtime performs additional checks.
// The logic below is the inlined copy of ObjectFromHandle in the unmanaged runtime.
#pragma warning disable 8500 // address of managed types
private static unsafe object? InternalGetTarget(IntPtr dependentHandle) => *(object*)dependentHandle;
private static unsafe object? InternalGetTarget(nint dependentHandle) => *(object*)dependentHandle;
#pragma warning restore 8500
#endif

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? InternalGetDependent(IntPtr dependentHandle);
private static extern object? InternalGetDependent(nint dependentHandle);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? InternalGetTargetAndDependent(IntPtr dependentHandle, out object? dependent);
private static extern object? InternalGetTargetAndDependent(nint dependentHandle, out object? dependent);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void InternalSetDependent(IntPtr dependentHandle, object? dependent);
private static extern void InternalSetDependent(bool isDeferFinalize, nint dependentHandle, object? dependent);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void InternalSetTargetToNull(IntPtr dependentHandle);
private static extern void InternalSetTargetToNull(nint dependentHandle);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool InternalFree(IntPtr dependentHandle);
private static extern bool InternalFree(bool isDeferFinalize, IntPtr dependentHandle);

[MethodImpl(MethodImplOptions.NoInlining)]
private static void InternalFreeWithGCTransition(IntPtr dependentHandle)
=> _InternalFreeWithGCTransition(dependentHandle);
private static void InternalFreeWithGCTransition(bool isDeferFinalize, IntPtr dependentHandle)
=> _InternalFreeWithGCTransition(isDeferFinalize, dependentHandle);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DependentHandle_InternalFreeWithGCTransition")]
private static partial void _InternalFreeWithGCTransition(IntPtr dependentHandle);
private static partial void _InternalFreeWithGCTransition([MarshalAs(UnmanagedType.Bool)] bool isDeferFinalize, IntPtr dependentHandle);
}
}
2 changes: 1 addition & 1 deletion src/coreclr/debug/daccess/daccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7740,7 +7740,7 @@ void CALLBACK DacHandleWalker::EnumCallback(PTR_UNCHECKED_OBJECTREF handle, uint

data.Handle = TO_CDADDR(handle.GetAddr());
data.Type = param->Type;
if (param->Type == HNDTYPE_DEPENDENT)
if (param->Type == HNDTYPE_DEPENDENT || param->Type == HNDTYPE_DEPENDENT_DEFER_FINALIZE)
data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr();
else
data.Secondary = 0;
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7582,6 +7582,9 @@ UINT32 DacRefWalker::GetHandleWalkerMask()
if (mHandleMask & CorHandleStrongSizedByref)
result |= (1 << HNDTYPE_SIZEDREF);

if (mHandleMask & CorHandleStrongDependentDeferFinalize)
result |= (1 << HNDTYPE_DEPENDENT_DEFER_FINALIZE);

return result;
}

Expand Down Expand Up @@ -7721,6 +7724,11 @@ HRESULT DacHandleWalker::Next(ULONG count, DacGcReference roots[], ULONG *pFetch
case HNDTYPE_SIZEDREF:
roots[i].dwType = (DWORD)CorHandleStrongSizedByref;
break;

case HNDTYPE_DEPENDENT_DEFER_FINALIZE:
roots[i].dwType = (DWORD)CorHandleStrongDependentDeferFinalize;
roots[i].i64ExtraData = GetDependentHandleSecondary(CLRDATA_ADDRESS_TO_TADDR(handle.Handle)).GetAddr();
break;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3255,7 +3255,7 @@ ClrDataAccess::GetThreadLocalModuleData(CLRDATA_ADDRESS thread, unsigned int ind
HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum)
{
unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_DEPENDENT,
HNDTYPE_SIZEDREF,
HNDTYPE_SIZEDREF, HNDTYPE_DEPENDENT_DEFER_FINALIZE,
#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
HNDTYPE_REFCOUNTED,
#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
Expand Down Expand Up @@ -3293,7 +3293,7 @@ HRESULT ClrDataAccess::GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppH
SOSDacEnter();

unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_DEPENDENT,
HNDTYPE_SIZEDREF,
HNDTYPE_SIZEDREF, HNDTYPE_DEPENDENT_DEFER_FINALIZE,
#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
HNDTYPE_REFCOUNTED,
#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
Expand Down
14 changes: 8 additions & 6 deletions src/coreclr/gc/gchandletable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,18 @@ OBJECTHANDLE GCHandleStore::CreateHandleWithExtraInfo(Object* object, HandleType
return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object), reinterpret_cast<uintptr_t>(pExtraInfo));
}

OBJECTHANDLE GCHandleStore::CreateDependentHandle(Object* primary, Object* secondary)
OBJECTHANDLE GCHandleStore::CreateDependentHandle(HandleType type, Object* primary, Object* secondary)
{
_ASSERTE(type == HNDTYPE_DEPENDENT || type == HNDTYPE_DEPENDENT_DEFER_FINALIZE);

HHANDLETABLE handletable = _underlyingBucket.pTable[GetCurrentThreadHomeHeapNumber()];
OBJECTHANDLE handle = ::HndCreateHandle(handletable, HNDTYPE_DEPENDENT, ObjectToOBJECTREF(primary));
OBJECTHANDLE handle = ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(primary));
if (!handle)
{
return nullptr;
}

::SetDependentHandleSecondary(handle, ObjectToOBJECTREF(secondary));
::SetDependentHandleSecondary(type, handle, ObjectToOBJECTREF(secondary));
return handle;
}

Expand Down Expand Up @@ -195,9 +197,9 @@ bool GCHandleManager::StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* obj
return !!::HndFirstAssignHandle(handle, ObjectToOBJECTREF(object));
}

void GCHandleManager::SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object)
void GCHandleManager::SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, Object* object)
{
::SetDependentHandleSecondary(handle, ObjectToOBJECTREF(object));
::SetDependentHandleSecondary(type, handle, ObjectToOBJECTREF(object));
}

Object* GCHandleManager::GetDependentHandleSecondary(OBJECTHANDLE handle)
Expand All @@ -213,7 +215,7 @@ Object* GCHandleManager::InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE h
HandleType GCHandleManager::HandleFetchType(OBJECTHANDLE handle)
{
uint32_t type = ::HandleFetchType(handle);
assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_NATIVE_COM);
assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_DEPENDENT_DEFER_FINALIZE);
return static_cast<HandleType>(type);
}

Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/gc/gchandletableimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class GCHandleStore : public IGCHandleStore

virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, HandleType type, void* pExtraInfo);

virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary);
virtual OBJECTHANDLE CreateDependentHandle(HandleType type, Object* primary, Object* secondary);

virtual ~GCHandleStore();

Expand Down Expand Up @@ -61,7 +61,7 @@ class GCHandleManager : public IGCHandleManager

virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object);

virtual void SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object);
virtual void SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, Object* object);

virtual Object* GetDependentHandleSecondary(OBJECTHANDLE handle);

Expand Down
18 changes: 14 additions & 4 deletions src/coreclr/gc/gcinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// The major version of the IGCHeap interface. Breaking changes to this interface
// require bumps in the major version number.
#define GC_INTERFACE_MAJOR_VERSION 5
#define GC_INTERFACE_MAJOR_VERSION 6

// The minor version of the IGCHeap interface. Non-breaking changes are required
// to bump the minor version number. GCs and EEs with minor version number
Expand Down Expand Up @@ -502,7 +502,17 @@ typedef enum
* but we are keeping it here for backward compatibility purposes"
*
*/
HNDTYPE_WEAK_NATIVE_COM = 9
HNDTYPE_WEAK_NATIVE_COM = 9,

/*
* DEPENDENT DEFER FINIALIZE HANDLES
*
* Like HNDTYPE_DEPENDENT, but the secondary is always promoted until
* the primary is collected.
*
*
*/
HNDTYPE_DEPENDENT_DEFER_FINALIZE = 10
} HandleType;

typedef enum
Expand Down Expand Up @@ -543,7 +553,7 @@ class IGCHandleStore {

virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, HandleType type, void* pExtraInfo) PURE_VIRTUAL

virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) PURE_VIRTUAL
virtual OBJECTHANDLE CreateDependentHandle(HandleType type, Object* primary, Object* secondary) PURE_VIRTUAL

virtual ~IGCHandleStore() {};
};
Expand Down Expand Up @@ -577,7 +587,7 @@ class IGCHandleManager {

virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL

virtual void SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL
virtual void SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, Object* object) PURE_VIRTUAL

virtual Object* GetDependentHandleSecondary(OBJECTHANDLE handle) PURE_VIRTUAL

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/gc/handletable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ void HndWriteBarrierWorker(OBJECTHANDLE handle, _UNCHECKED_OBJECTREF value)
}
#endif

if (uType == HNDTYPE_DEPENDENT)
if (uType == HNDTYPE_DEPENDENT || uType == HNDTYPE_DEPENDENT_DEFER_FINALIZE)
{
generation = 0;
}
Expand Down
Loading