Skip to content

Commit

Permalink
Remove Helper Method Frames for Exception, GC and Thread methods (#10…
Browse files Browse the repository at this point in the history
…7218)

* Convert Exception.GetFrozenStackTrace()

* Convert GC.AllocateNewArray()
Removed use of Unsafe.As().

* Convert Thread.GetApartmentStateNative() and Thread.SetApartmentStateNative()

* Convert Thread.Join()

* Convert Thread.Priority property
  • Loading branch information
AaronRobinsonMSFT committed Sep 6, 2024
1 parent 0fa7321 commit 5428078
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 279 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,6 @@ internal void InternalPreserveStackTrace()
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void PrepareForForeignExceptionRaise();

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? GetFrozenStackTrace(Exception exception);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern uint GetExceptionCount();

Expand Down Expand Up @@ -226,9 +223,14 @@ public DispatchState(
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ExceptionNative_GetFrozenStackTrace")]
private static partial void GetFrozenStackTrace(ObjectHandleOnStack exception, ObjectHandleOnStack stackTrace);

internal DispatchState CaptureDispatchState()
{
object? stackTrace = GetFrozenStackTrace(this);
Exception _this = this;
object? stackTrace = null;
GetFrozenStackTrace(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref stackTrace));

return new DispatchState(stackTrace,
_remoteStackTraceString, _ipForWatsonBuckets, _watsonBuckets);
Expand Down
29 changes: 20 additions & 9 deletions src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ internal enum GC_ALLOC_FLAGS
GC_ALLOC_PINNED_OBJECT_HEAP = 64,
};

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Array AllocateNewArray(IntPtr typeHandle, int length, GC_ALLOC_FLAGS flags);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_AllocateNewArray")]
private static partial void AllocateNewArray(IntPtr typeHandlePtr, int length, GC_ALLOC_FLAGS flags, ObjectHandleOnStack ret);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_GetTotalMemory")]
private static partial long GetTotalMemory();
Expand Down Expand Up @@ -791,16 +791,25 @@ public static unsafe T[] AllocateUninitializedArray<T>(int length, bool pinned =
{
return new T[length];
}

#endif
}

// Runtime overrides GC_ALLOC_ZEROING_OPTIONAL if the type contains references, so we don't need to worry about that.
GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL;
if (pinned)
flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP;
return AllocateNewArrayWorker(length, pinned);

[MethodImpl(MethodImplOptions.NoInlining)]
static T[] AllocateNewArrayWorker(int length, bool pinned)
{
// Runtime overrides GC_ALLOC_ZEROING_OPTIONAL if the type contains references, so we don't need to worry about that.
GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL;
if (pinned)
{
flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP;
}

return Unsafe.As<T[]>(AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags));
T[]? result = null;
AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags, ObjectHandleOnStack.Create(ref result));
return result!;
}
}

/// <summary>
Expand All @@ -818,7 +827,9 @@ public static T[] AllocateArray<T>(int length, bool pinned = false) // T[] rathe
flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP;
}

return Unsafe.As<T[]>(AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags));
T[]? result = null;
AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags, ObjectHandleOnStack.Create(ref result));
return result!;
}

[MethodImpl(MethodImplOptions.InternalCall)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public sealed partial class Thread
// but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized.
private bool _mayNeedResetForThreadPool;

// Set in unmanaged and read in managed code.
private bool _isDead;

private Thread() { }

public int ManagedThreadId
Expand All @@ -74,7 +77,7 @@ internal ThreadHandle GetNativeHandle()
// This should never happen under normal circumstances.
if (thread == IntPtr.Zero)
{
throw new ArgumentException(null, SR.Argument_InvalidHandle);
throw new ThreadStateException(SR.Argument_InvalidHandle);
}

return new ThreadHandle(thread);
Expand Down Expand Up @@ -211,26 +214,32 @@ public extern bool IsThreadPoolThread
internal set;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetPriority")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial void SetPriority(ObjectHandleOnStack thread, int priority);

/// <summary>Returns the priority of the thread.</summary>
public ThreadPriority Priority
{
get => (ThreadPriority)GetPriorityNative();
get
{
if (_isDead)
{
throw new ThreadStateException(SR.ThreadState_Dead_Priority);
}
return (ThreadPriority)_priority;
}
set
{
SetPriorityNative((int)value);
Thread _this = this;
SetPriority(ObjectHandleOnStack.Create(ref _this), (int)value);
if (value != ThreadPriority.Normal)
{
_mayNeedResetForThreadPool = true;
}
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
private extern int GetPriorityNative();

[MethodImpl(MethodImplOptions.InternalCall)]
private extern void SetPriorityNative(int priority);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentOSThreadId")]
private static partial ulong GetCurrentOSThreadId();

Expand All @@ -243,21 +252,31 @@ public ThreadPriority Priority
[MethodImpl(MethodImplOptions.InternalCall)]
private extern int GetThreadStateNative();

public ApartmentState GetApartmentState() =>
#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
(ApartmentState)GetApartmentStateNative();
#else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT
ApartmentState.Unknown;
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT

/// <summary>
/// An unstarted thread can be marked to indicate that it will host a
/// single-threaded or multi-threaded apartment.
/// </summary>
#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetApartmentState")]
private static partial int GetApartmentState(ObjectHandleOnStack t);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetApartmentState")]
private static partial int SetApartmentState(ObjectHandleOnStack t, int state);

public ApartmentState GetApartmentState()
{
Thread _this = this;
return (ApartmentState)GetApartmentState(ObjectHandleOnStack.Create(ref _this));
}

private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
{
ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state);
ApartmentState retState;
lock (this) // This lock is only needed when the this is not the current thread.
{
Thread _this = this;
retState = (ApartmentState)SetApartmentState(ObjectHandleOnStack.Create(ref _this), (int)state);
}

// Special case where we pass in Unknown and get back MTA.
// Once we CoUninitialize the thread, the OS will still
Expand All @@ -282,12 +301,9 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
return true;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern int GetApartmentStateNative();

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern int SetApartmentStateNative(int state);
#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT
public ApartmentState GetApartmentState() => ApartmentState.Unknown;

private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
{
if (state != ApartmentState.Unknown)
Expand Down Expand Up @@ -331,18 +347,31 @@ public void Interrupt()
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Interrupt")]
private static partial void Interrupt(ThreadHandle t);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Join")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool Join(ObjectHandleOnStack thread, int millisecondsTimeout);

/// <summary>
/// Waits for the thread to die or for timeout milliseconds to elapse.
/// </summary>
/// <returns>
/// Returns true if the thread died, or false if the wait timed out. If
/// -1 is given as the parameter, no timeout will occur.
/// </returns>
/// <exception cref="ArgumentException">if timeout &lt; -1 (Timeout.Infinite)</exception>
/// <exception cref="ArgumentOutOfRangeException">if timeout &lt; -1 (Timeout.Infinite)</exception>
/// <exception cref="ThreadInterruptedException">if the thread is interrupted while waiting</exception>
/// <exception cref="ThreadStateException">if the thread has not been started yet</exception>
[MethodImpl(MethodImplOptions.InternalCall)]
public extern bool Join(int millisecondsTimeout);
public bool Join(int millisecondsTimeout)
{
// Validate the timeout
if (millisecondsTimeout < 0 && millisecondsTimeout != Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
}

Thread _this = this;
return Join(ObjectHandleOnStack.Create(ref _this), millisecondsTimeout);
}

/// <summary>
/// Max value to be passed into <see cref="SpinWait(int)"/> for optimal delaying. This value is normalized to be
Expand Down
Loading

0 comments on commit 5428078

Please sign in to comment.