Skip to content

Commit

Permalink
Remove some implicit Helper Method Frames (HMF) (#107648)
Browse files Browse the repository at this point in the history
* Remove some implicit Helper Method Frames (HMF)

Remove FCThrow in locations where explicit HMF have been removed.
Convert GC.GetGeneration()
RuntimeHelpers.EnsureSufficientExecutionStack()
RuntimeHelpers.AllocTailCallArgBuffer()
Thread.IsBackground getter
Thread.IsThreadPoolThread property

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
  • Loading branch information
AaronRobinsonMSFT and jkotas committed Sep 24, 2024
1 parent 7dec452 commit 3ab61c2
Show file tree
Hide file tree
Showing 19 changed files with 130 additions and 219 deletions.
10 changes: 7 additions & 3 deletions src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,16 @@ public static void RemoveMemoryPressure(long bytesAllocated)
_RemoveMemoryPressure((ulong)bytesAllocated);
}


// Returns the generation that obj is currently in.
//
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern int GetGeneration(object obj);
public static int GetGeneration(object obj)
{
ArgumentNullException.ThrowIfNull(obj);
return GetGenerationInternal(obj);
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int GetGenerationInternal(object obj);

// Forces a collection of all generations from 0 through Generation.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,18 @@ public static int OffsetToStringData

// This method ensures that there is sufficient stack to execute the average Framework function.
// If there is not enough stack, then it throws System.InsufficientExecutionStackException.
// Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack.
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void EnsureSufficientExecutionStack();
// Note: this method is not to be confused with ProbeForSufficientStack.
public static void EnsureSufficientExecutionStack()
{
if (!TryEnsureSufficientExecutionStack())
{
throw new InsufficientExecutionStackException();
}
}

// This method ensures that there is sufficient stack to execute the average Framework function.
// If there is not enough stack, then it return false.
// Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack.
// Note: this method is not to be confused with ProbeForSufficientStack.
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool TryEnsureSufficientExecutionStack();

Expand Down Expand Up @@ -469,7 +474,17 @@ public static IntPtr AllocateTypeAssociatedMemory(Type type, int size)
private static partial IntPtr AllocateTypeAssociatedMemory(QCallTypeHandle type, uint size);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr AllocTailCallArgBuffer(int size, IntPtr gcDesc);
private static extern IntPtr AllocTailCallArgBufferWorker(int size, IntPtr gcDesc);

private static IntPtr AllocTailCallArgBuffer(int size, IntPtr gcDesc)
{
IntPtr buffer = AllocTailCallArgBufferWorker(size, gcDesc);
if (buffer == IntPtr.Zero)
{
throw new OutOfMemoryException();
}
return buffer;
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe TailCallTls* GetTailCallInfo(IntPtr retAddrSlot, IntPtr* retAddr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public sealed partial class Thread

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

private Thread() { }

Expand Down Expand Up @@ -89,13 +90,13 @@ private unsafe void StartCore()
{
fixed (char* pThreadName = _name)
{
StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName);
StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, _isThreadPool ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, pThreadName);
}
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")]
private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName);
private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, Interop.BOOL isThreadPool, char* pThreadName);

// Called from the runtime
private void StartCallback()
Expand Down Expand Up @@ -194,9 +195,24 @@ partial void ThreadNameChanged(string? value)
/// </summary>
public bool IsBackground
{
get => GetIsBackground();
get
{
if (_isDead)
{
throw new ThreadStateException(SR.ThreadState_Dead_State);
}

Interop.BOOL res = GetIsBackground(GetNativeHandle());
GC.KeepAlive(this);
return res != Interop.BOOL.FALSE;
}
set
{
if (_isDead)
{
throw new ThreadStateException(SR.ThreadState_Dead_State);
}

SetIsBackground(GetNativeHandle(), value ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);
GC.KeepAlive(this);
if (!value)
Expand All @@ -206,19 +222,36 @@ public bool IsBackground
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool GetIsBackground();
[SuppressGCTransition]
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetIsBackground")]
private static partial Interop.BOOL GetIsBackground(ThreadHandle t);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetIsBackground")]
private static partial void SetIsBackground(ThreadHandle t, Interop.BOOL value);

/// <summary>Returns true if the thread is a threadpool thread.</summary>
public extern bool IsThreadPoolThread
public bool IsThreadPoolThread
{
[MethodImpl(MethodImplOptions.InternalCall)]
get;
[MethodImpl(MethodImplOptions.InternalCall)]
internal set;
get
{
if (_isDead)
{
throw new ThreadStateException(SR.ThreadState_Dead_State);
}

return _isThreadPool;
}
internal set
{
Debug.Assert(value);
Debug.Assert(!_isDead);
Debug.Assert(((ThreadState & ThreadState.Unstarted) != 0)
#if TARGET_WINDOWS
|| ThreadPool.UseWindowsThreadPool
#endif
);
_isThreadPool = value;
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetPriority")]
Expand Down
122 changes: 35 additions & 87 deletions src/coreclr/vm/comsynchronizable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,17 @@ INT32 MapFromNTPriority(INT32 NTPriority)
return ours;
}


void ThreadNative::KickOffThread_Worker(LPVOID ptr)
static void KickOffThread_Worker(LPVOID ptr)
{
CONTRACTL
{
GC_TRIGGERS;
THROWS;
MODE_COOPERATIVE;
PRECONDITION(ptr == NULL);
}
CONTRACTL_END;

KickOffThread_Args *pKickOffArgs = (KickOffThread_Args *) ptr;
pKickOffArgs->retVal = 0;

PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__START_CALLBACK);
DECLARE_ARGHOLDER_ARRAY(args, 1);
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(GetThread()->GetExposedObjectRaw());
Expand Down Expand Up @@ -167,7 +164,7 @@ static void PulseAllHelper(Thread* pThread)
}

// When an exposed thread is started by Win32, this is where it starts.
ULONG WINAPI ThreadNative::KickOffThread(void* pass)
static ULONG WINAPI KickOffThread(void* pass)
{

CONTRACTL
Expand Down Expand Up @@ -204,11 +201,7 @@ ULONG WINAPI ThreadNative::KickOffThread(void* pass)

_ASSERTE(GetThread() == pThread); // Now that it's started

KickOffThread_Args args;
args.share = NULL;
args.pThread = pThread;

ManagedThreadBase::KickOff(KickOffThread_Worker, &args);
ManagedThreadBase::KickOff(KickOffThread_Worker, NULL);

PulseAllHelper(pThread);

Expand All @@ -222,21 +215,13 @@ ULONG WINAPI ThreadNative::KickOffThread(void* pass)
return 0;
}

extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, PCWSTR pThreadName)
extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName)
{
QCALL_CONTRACT;

BEGIN_QCALL;

ThreadNative::Start(thread, threadStackSize, priority, pThreadName);

END_QCALL;
}

void ThreadNative::Start(Thread* pNewThread, int threadStackSize, int priority, PCWSTR pThreadName)
{
STANDARD_VM_CONTRACT;

Thread* pNewThread = thread;
_ASSERTE(pNewThread != NULL);

// Is the thread already started? You can't restart a thread.
Expand Down Expand Up @@ -283,6 +268,8 @@ void ThreadNative::Start(Thread* pNewThread, int threadStackSize, int priority,
pNewThread->ChooseThreadCPUGroupAffinity();

pNewThread->SetThreadState(Thread::TS_LegalToJoin);
if (isThreadPool)
pNewThread->SetIsThreadPoolThread();

DWORD ret = pNewThread->StartThread();

Expand Down Expand Up @@ -311,6 +298,8 @@ void ThreadNative::Start(Thread* pNewThread, int threadStackSize, int priority,
PulseAllHelper(pNewThread);
pNewThread->HandleThreadStartupFailure();
}

END_QCALL;
}

extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority)
Expand Down Expand Up @@ -419,24 +408,6 @@ extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t)
END_QCALL;
}

// Return whether or not this is a background thread.
FCIMPL1(FC_BOOL_RET, ThreadNative::GetIsBackground, ThreadBaseObject* pThisUNSAFE)
{
FCALL_CONTRACT;

if (pThisUNSAFE==NULL)
FCThrowRes(kNullReferenceException, W("NullReference_This"));

// validate the thread
Thread *thread = pThisUNSAFE->GetInternal();

if (ThreadIsDead(thread))
FCThrowRes(kThreadStateException, W("ThreadState_Dead_State"));

FC_RETURN_BOOL(thread->IsBackground());
}
FCIMPLEND

// Deliver the state of the thread as a consistent set of bits.
// Duplicate logic in DacDbiInterfaceImpl::GetPartialUserState()
extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread)
Expand Down Expand Up @@ -743,6 +714,27 @@ FCIMPL1(void, ThreadNative::Finalize, ThreadBaseObject* pThisUNSAFE)
}
FCIMPLEND

// Get whether or not this is a background thread.
extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle thread)
{
CONTRACTL
{
QCALL_CHECK_NO_GC_TRANSITION;
PRECONDITION(thread != NULL);
}
CONTRACTL_END;

BOOL res = FALSE;

BEGIN_QCALL;

res = thread->IsBackground();

END_QCALL;

return res;
}

// Set whether or not this is a background thread.
extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle thread, BOOL value)
{
Expand All @@ -755,9 +747,6 @@ extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle threa

BEGIN_QCALL;

if (ThreadIsDead(thread))
COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State"));

thread->SetBackground(value);

END_QCALL;
Expand All @@ -769,16 +758,10 @@ extern "C" void QCALLTYPE ThreadNative_InformThreadNameChange(QCall::ThreadHandl

BEGIN_QCALL;

ThreadNative::InformThreadNameChange(thread, name, len);

END_QCALL;
}
Thread* pThread = thread;

void ThreadNative::InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 len)
{
// Set on Windows 10 Creators Update and later machines the unmanaged thread name as well. That will show up in ETW traces and debuggers which is very helpful
// if more and more threads get a meaningful name
// Will also show up in Linux in gdb and such.
// The name will show up in ETW traces and debuggers which is very helpful if more and more threads
// get a meaningful name. Will also show up in Linux in gdb and such.
if (len > 0 && name != NULL && pThread->GetThreadHandle() != INVALID_HANDLE_VALUE)
{
SetThreadName(pThread->GetThreadHandle(), name);
Expand All @@ -799,51 +782,16 @@ void ThreadNative::InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 l
}
#endif // PROFILING_SUPPORTED


#ifdef DEBUGGING_SUPPORTED
if (CORDebuggerAttached())
{
_ASSERTE(NULL != g_pDebugInterface);
g_pDebugInterface->NameChangeEvent(NULL, pThread);
}
#endif // DEBUGGING_SUPPORTED
}

FCIMPL1(FC_BOOL_RET, ThreadNative::IsThreadpoolThread, ThreadBaseObject* thread)
{
FCALL_CONTRACT;

if (thread==NULL)
FCThrowRes(kNullReferenceException, W("NullReference_This"));

Thread *pThread = thread->GetInternal();

if (pThread == NULL)
FCThrowRes(kThreadStateException, W("ThreadState_Dead_State"));

BOOL ret = pThread->IsThreadPoolThread();

FC_GC_POLL_RET();

FC_RETURN_BOOL(ret);
}
FCIMPLEND

FCIMPL1(void, ThreadNative::SetIsThreadpoolThread, ThreadBaseObject* thread)
{
FCALL_CONTRACT;

if (thread == NULL)
FCThrowResVoid(kNullReferenceException, W("NullReference_This"));

Thread *pThread = thread->GetInternal();

if (pThread == NULL)
FCThrowResVoid(kThreadStateException, W("ThreadState_Dead_State"));

pThread->SetIsThreadPoolThread();
END_QCALL;
}
FCIMPLEND

FCIMPL0(INT32, ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
{
Expand Down
Loading

0 comments on commit 3ab61c2

Please sign in to comment.