Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4912b81
Add GCHandle<T>
huoyaoyuan Jan 11, 2025
34667f6
Implementation and documentation
huoyaoyuan Jan 11, 2025
0b92685
PinnedGCHandle
huoyaoyuan Jan 11, 2025
b533663
Add missing setters
huoyaoyuan Jan 11, 2025
a443880
WeakGCHandle
huoyaoyuan Jan 11, 2025
37c22a5
GCHandleExtensions
huoyaoyuan Jan 11, 2025
7e9dcab
Reference source
huoyaoyuan Jan 11, 2025
daa2d71
Unit test
huoyaoyuan Jan 11, 2025
3d6c6cc
Only allows pinnable object
huoyaoyuan Jan 11, 2025
6e3a26e
Fix unit tests
huoyaoyuan Jan 11, 2025
52a892e
Don't change file ending
huoyaoyuan Jan 11, 2025
cd45974
Maximize unsafety
huoyaoyuan Jan 11, 2025
06f7298
Adjust wording
huoyaoyuan Jan 11, 2025
af5be56
Remove interlock
huoyaoyuan Jan 11, 2025
948b665
Allow pinning any object
huoyaoyuan Jan 11, 2025
4c82bfe
Nullable oblivious
huoyaoyuan Jan 11, 2025
80a8580
Reduce nullable oblivious range
huoyaoyuan Jan 11, 2025
c25bf1b
Avoid extra overhead of null check
huoyaoyuan Jan 11, 2025
8dee06a
Reduce overhead of GetAddressOfArrayData and GetAddressOfStringData
huoyaoyuan Jan 11, 2025
e73f07c
Update null check
huoyaoyuan Jan 12, 2025
433047e
Reduce reads from Dispose
huoyaoyuan Jan 12, 2025
47b5017
Add equality
huoyaoyuan Jan 12, 2025
39752af
Update comment
huoyaoyuan Jan 13, 2025
5510296
Remove equality operators
huoyaoyuan Jan 13, 2025
d3a3a28
Update semantic of GetAddressOfObjectData
huoyaoyuan Jan 13, 2025
0a8485d
Update xmldoc
huoyaoyuan Jan 13, 2025
86e9d93
Remove UnsafeGCHandle
huoyaoyuan Jan 13, 2025
cf4add9
Update usages in CoreLib
huoyaoyuan Jan 13, 2025
5907d3c
Update usages in NativeAot CoreLib
huoyaoyuan Jan 13, 2025
1b0bf88
Cleanup leaked handle in R2R
huoyaoyuan Jan 13, 2025
3b1a097
Update usages in CryptoStream
huoyaoyuan Jan 13, 2025
ec98a74
Add xmldoc for exception
huoyaoyuan Jan 13, 2025
cf45bcd
Fix constraint
huoyaoyuan Jan 14, 2025
3598aa0
Fix xmldoc
huoyaoyuan Jan 14, 2025
1337d04
Fix build
huoyaoyuan Jan 14, 2025
43859e2
Merge branch 'main' into gchandle
huoyaoyuan Jan 15, 2025
6c80e1b
Fix unintended change
huoyaoyuan Jan 15, 2025
c8a9497
Revert CryptoStream
huoyaoyuan Jan 15, 2025
f8d0158
Update xmldoc
huoyaoyuan Jan 15, 2025
42c6362
Update src/libraries/System.Private.CoreLib/src/System/Runtime/Intero…
jkotas Jan 16, 2025
d7800bf
Merge branch 'main' into gchandle
jkotas Feb 10, 2025
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
9 changes: 4 additions & 5 deletions src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ private unsafe struct NoGCRegionCallbackFinalizerWorkItem
public bool scheduled;
public bool abandoned;

public GCHandle action;
public GCHandle<Action> action;
}

internal enum EnableNoGCRegionCallbackStatus
Expand Down Expand Up @@ -709,7 +709,7 @@ public static unsafe void RegisterNoGCRegionCallback(long totalSize, Action call
try
{
pWorkItem = (NoGCRegionCallbackFinalizerWorkItem*)NativeMemory.AllocZeroed((nuint)sizeof(NoGCRegionCallbackFinalizerWorkItem));
pWorkItem->action = GCHandle.Alloc(callback);
pWorkItem->action = new GCHandle<Action>(callback);
pWorkItem->callback = &Callback;

EnableNoGCRegionCallbackStatus status = (EnableNoGCRegionCallbackStatus)_EnableNoGCRegionCallback(pWorkItem, totalSize);
Expand Down Expand Up @@ -739,14 +739,13 @@ static void Callback(NoGCRegionCallbackFinalizerWorkItem* pWorkItem)
{
Debug.Assert(pWorkItem->scheduled);
if (!pWorkItem->abandoned)
((Action)(pWorkItem->action.Target!))();
pWorkItem->action.Target();
Free(pWorkItem);
}

static void Free(NoGCRegionCallbackFinalizerWorkItem* pWorkItem)
{
if (pWorkItem->action.IsAllocated)
pWorkItem->action.Free();
pWorkItem->action.Dispose();
NativeMemory.Free(pWorkItem);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer()
/// <summary>
/// Retrieves the current delegate that is being called
/// </summary>
public static T GetCurrentCalleeDelegate<T>() where T : class // constraint can't be System.Delegate
public static T GetCurrentCalleeDelegate<T>() where T : Delegate
{
return PInvokeMarshal.GetCurrentCalleeDelegate<T>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@
<Compile Include="System\Runtime\InteropServices\MemoryMarshal.NativeAot.cs" />
<Compile Include="System\Runtime\InteropServices\TrackerObjectManager.NativeAot.cs" Condition="'$(FeatureComWrappers)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\ObjectiveCMarshal.NativeAot.cs" Condition="'$(FeatureObjCMarshal)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\UnsafeGCHandle.cs" />
<Compile Include="System\Runtime\Intrinsics\X86\X86Base.NativeAot.cs" Condition="'$(SupportsX86Intrinsics)' == 'true'" />
<Compile Include="System\Runtime\JitInfo.NativeAot.cs" />
<Compile Include="System\Runtime\Loader\AssemblyLoadContext.NativeAot.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ private unsafe struct NoGCRegionCallbackFinalizerWorkItem
public bool scheduled;
public bool abandoned;

public GCHandle action;
public GCHandle<Action> action;
}

public static unsafe void RegisterNoGCRegionCallback(long totalSize, Action callback)
Expand All @@ -317,7 +317,7 @@ public static unsafe void RegisterNoGCRegionCallback(long totalSize, Action call
try
{
pWorkItem = (NoGCRegionCallbackFinalizerWorkItem*)NativeMemory.AllocZeroed((nuint)sizeof(NoGCRegionCallbackFinalizerWorkItem));
pWorkItem->action = GCHandle.Alloc(callback);
pWorkItem->action = new GCHandle<Action>(callback);
pWorkItem->callback = &Callback;

EnableNoGCRegionCallbackStatus status = (EnableNoGCRegionCallbackStatus)RuntimeImports.RhEnableNoGCRegionCallback(pWorkItem, totalSize);
Expand Down Expand Up @@ -347,14 +347,13 @@ static void Callback(NoGCRegionCallbackFinalizerWorkItem* pWorkItem)
{
Debug.Assert(pWorkItem->scheduled);
if (!pWorkItem->abandoned)
((Action)(pWorkItem->action.Target!))();
pWorkItem->action.Target();
Free(pWorkItem);
}

static void Free(NoGCRegionCallbackFinalizerWorkItem* pWorkItem)
{
if (pWorkItem->action.IsAllocated)
pWorkItem->action.Free();
pWorkItem->action.Dispose();
NativeMemory.Free(pWorkItem);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private static ConditionalWeakTable<Delegate, PInvokeDelegateThunk> GetPInvokeDe
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal unsafe struct ThunkContextData
{
public GCHandle Handle; // A weak GCHandle to the delegate
public WeakGCHandle<Delegate> Handle; // A weak GCHandle to the delegate
public IntPtr FunctionPtr; // Function pointer for open static delegates
}

Expand Down Expand Up @@ -133,7 +133,7 @@ public PInvokeDelegateThunk(Delegate del)
ThunkContextData* thunkData = (ThunkContextData*)ContextData;

// allocate a weak GChandle for the delegate
thunkData->Handle = GCHandle.Alloc(del, GCHandleType.WeakTrackResurrection);
thunkData->Handle = new WeakGCHandle<Delegate>(del, trackResurrection: true);
thunkData->FunctionPtr = openStaticFunctionPointer;
}

Expand All @@ -148,17 +148,17 @@ public PInvokeDelegateThunk(Delegate del)
if (ContextData != IntPtr.Zero)
{
// free the GCHandle
GCHandle handle = ((ThunkContextData*)ContextData)->Handle;
WeakGCHandle<Delegate> handle = ((ThunkContextData*)ContextData)->Handle;
if (handle.IsAllocated)
{
// If the delegate is still alive, defer finalization.
if (handle.Target != null)
if (handle.TryGetTarget(out _))
{
GC.ReRegisterForFinalize(this);
return;
}

handle.Free();
handle.Dispose();
}

// Free the allocated context data memory
Expand Down Expand Up @@ -205,21 +205,20 @@ private static unsafe PInvokeDelegateThunk AllocateThunk(Delegate del)
IntPtr pTarget;
if (s_thunkPoolHeap != null && RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, ptr, out pContext, out pTarget))
{
GCHandle handle;
WeakGCHandle<Delegate> handle;
unsafe
{
// Pull out Handle from context
handle = ((ThunkContextData*)pContext)->Handle;
}
Delegate target = Unsafe.As<Delegate>(handle.Target);

//
// The delegate might already been garbage collected
// User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive
// until they are done with the native function pointer
//
if (target == null)
if (!handle.TryGetTarget(out Delegate? target))
{
//
// The delegate might already been garbage collected
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The delegate might already been garbage collected
// The delegate might have already been garbage collected

Copy link
Member

@stephentoub stephentoub Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"might have"... what situation would result in TryGetTarget being false here but because of some reason other than GC?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment was copied from old one. Maybe it's saying in the context of the whole method?

// User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive
// until they are done with the native function pointer
//
Environment.FailFast(SR.Delegate_GarbageCollected);
}

Expand Down Expand Up @@ -269,7 +268,7 @@ public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer()
/// <summary>
/// Retrieves the current delegate that is being called
/// </summary>
public static T GetCurrentCalleeDelegate<T>() where T : class // constraint can't be System.Delegate
public static T GetCurrentCalleeDelegate<T>() where T : Delegate
{
//
// RH keeps track of the current thunk that is being called through a secret argument / thread
Expand All @@ -280,26 +279,25 @@ public static T GetCurrentCalleeDelegate<T>() where T : class // constraint can'

Debug.Assert(pContext != IntPtr.Zero);

GCHandle handle;
WeakGCHandle<Delegate> handle;
unsafe
{
// Pull out Handle from context
handle = ((ThunkContextData*)pContext)->Handle;

}

T target = Unsafe.As<T>(handle.Target);

//
// The delegate might already been garbage collected
// User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive
// until they are done with the native function pointer
//
if (target == null)
if (!handle.TryGetTarget(out Delegate? target))
{
//
// The delegate might already been garbage collected
// User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive
// until they are done with the native function pointer
//
Environment.FailFast(SR.Delegate_GarbageCollected);
}
return target;

return Unsafe.As<T>(target);
}
#endregion

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ private bool JoinInternal(int millisecondsTimeout)
}
}

private unsafe bool CreateThread(GCHandle thisThreadHandle)
private unsafe bool CreateThread(GCHandle<Thread> thisThreadHandle)
{
// Create the Stop event before starting the thread to make sure
// it is ready to be signaled at thread shutdown time.
// This also avoids OOM after creating the thread.
_stopped = new ManualResetEvent(false);

if (!Interop.Sys.CreateThread((IntPtr)_startHelper!._maxStackSize, &ThreadEntryPoint, (IntPtr)thisThreadHandle))
if (!Interop.Sys.CreateThread((IntPtr)_startHelper!._maxStackSize, &ThreadEntryPoint, GCHandle<Thread>.ToIntPtr(thisThreadHandle)))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private bool JoinInternal(int millisecondsTimeout)
}
}

private unsafe bool CreateThread(GCHandle thisThreadHandle)
private unsafe bool CreateThread(GCHandle<Thread> thisThreadHandle)
{
const int AllocationGranularity = 0x10000; // 64 KiB

Expand All @@ -197,7 +197,7 @@ private unsafe bool CreateThread(GCHandle thisThreadHandle)
}

_osHandle = Interop.Kernel32.CreateThread(IntPtr.Zero, (IntPtr)stackSize,
&ThreadEntryPoint, (IntPtr)thisThreadHandle,
&ThreadEntryPoint, GCHandle<Thread>.ToIntPtr(thisThreadHandle),
Interop.Kernel32.CREATE_SUSPENDED | Interop.Kernel32.STACK_SIZE_PARAM_IS_A_RESERVATION,
out _);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ private void StartCore()
}

bool waitingForThreadStart = false;
GCHandle threadHandle = GCHandle.Alloc(this);
GCHandle<Thread> threadHandle = new GCHandle<Thread>(this);

try
{
Expand All @@ -404,7 +404,7 @@ private void StartCore()
Debug.Assert(!waitingForThreadStart, "Leaked threadHandle");
if (!waitingForThreadStart)
{
threadHandle.Free();
threadHandle.Dispose();
}
}

Expand All @@ -422,8 +422,7 @@ private void StartCore()

private static void StartThread(IntPtr parameter)
{
GCHandle threadHandle = (GCHandle)parameter;
Thread thread = (Thread)threadHandle.Target!;
Thread thread = GCHandle<Thread>.FromIntPtr(parameter).Target;

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,17 @@ public static class TypeSystemContextFactory
{
// Cache the most recent instance of TypeSystemContext in a weak handle, and reuse it if possible
// This allows us to avoid recreating the type resolution context again and again, but still allows it to go away once the types are no longer being built
private static GCHandle s_cachedContext = GCHandle.Alloc(null, GCHandleType.Weak);
private static WeakGCHandle<TypeSystemContext?> s_cachedContext = new WeakGCHandle<TypeSystemContext?>(null);

private static readonly Lock s_lock = new Lock(useTrivialWaits: true);

public static TypeSystemContext Create()
{
using (s_lock.EnterScope())
{
TypeSystemContext context = (TypeSystemContext)s_cachedContext.Target;
if (context != null)
if (s_cachedContext.TryGetTarget(out TypeSystemContext? context))
{
s_cachedContext.Target = null;
s_cachedContext.SetTarget(null);
return context;
}
}
Expand Down Expand Up @@ -63,7 +62,7 @@ public static void Recycle(TypeSystemContext context)
context.FlushTypeBuilderStates();

// No lock needed here - the reference assignment is atomic
s_cachedContext.Target = context;
s_cachedContext.SetTarget(context);
}
}
}
Loading
Loading