diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 7843c86f1ba81..9b1b610951f8b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -177,7 +177,7 @@ private void Initialize() [MethodImpl(MethodImplOptions.InternalCall)] private extern void InternalFinalize(); - partial void ThreadNameChanged(string? value) + private void ThreadNameChanged(string? value) { InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); GC.KeepAlive(this); diff --git a/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp b/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp index b0f9eb0db5aa9..7f49f0830c241 100644 --- a/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp @@ -39,6 +39,8 @@ uint32_t WINAPI FinalizerStart(void* pContext) { HANDLE hFinalizerEvent = (HANDLE)pContext; + PalSetCurrentThreadName(".NET Finalizer"); + ThreadStore::AttachCurrentThread(); Thread * pThread = ThreadStore::GetCurrentThread(); diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index 22f12552530f8..9f613d13a8b1e 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -762,6 +762,10 @@ REDHAWK_PALIMPORT void REDHAWK_PALAPI PalSetHardwareExceptionHandler(PHARDWARE_E #endif typedef uint32_t (__stdcall *BackgroundCallback)(_In_opt_ void* pCallbackContext); +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalSetCurrentThreadName(const char* name); +#ifdef TARGET_WINDOWS +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalSetCurrentThreadNameW(const WCHAR* name); +#endif REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartFinalizerThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartEventPipeHelperThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp index e54156891593f..1af3e334d2b63 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp @@ -340,6 +340,11 @@ ep_rt_aot_get_last_error (void) return PalGetLastError(); } +void ep_rt_aot_set_server_name (void) +{ + PalSetCurrentThreadName(".NET EventPipe"); +} + bool ep_rt_aot_thread_create ( void *thread_func, @@ -361,7 +366,7 @@ ep_rt_aot_thread_create ( case EP_THREAD_TYPE_SERVER: // Match CoreCLR and hardcode a null thread context in this case. - return PalStartEventPipeHelperThread(reinterpret_cast(thread_func), NULL); + return PalStartEventPipeHelperThread(reinterpret_cast(thread_func), nullptr); case EP_THREAD_TYPE_SESSION: case EP_THREAD_TYPE_SAMPLING: diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h index 466fd369be941..0513409886ebb 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h @@ -760,7 +760,9 @@ inline void ep_rt_set_server_name(void) { - // This is optional, decorates the thread name with EventPipe specific information + extern void + ep_rt_aot_set_server_name (void); + ep_rt_aot_set_server_name (); } diff --git a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp index 7f1baa805111b..7f304f9a4335f 100644 --- a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp +++ b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp @@ -534,18 +534,33 @@ struct ThreadStubArguments void (*m_pRealStartRoutine)(void*); void* m_pRealContext; CLREventStatic m_ThreadStartedEvent; + const char* m_name; }; static bool CreateNonSuspendableThread(void (*threadStart)(void*), void* arg, const char* name) { - UNREFERENCED_PARAMETER(name); - ThreadStubArguments* threadStubArgs = new (nothrow) ThreadStubArguments(); if (!threadStubArgs) return false; threadStubArgs->m_pRealStartRoutine = threadStart; threadStubArgs->m_pRealContext = arg; + if (name == nullptr) + { + threadStubArgs->m_name = nullptr; + } + else + { + size_t name_length = strlen(name); + char* name_copy = new (nothrow) char[name_length + 1]; + if (name_copy == nullptr) + { + delete threadStubArgs; + return false; + } + strcpy(name_copy, name); + threadStubArgs->m_name = name_copy; + } // Helper used to wrap the start routine of GC threads so we can do things like initialize the // thread state which requires running in the new thread's context. @@ -554,6 +569,7 @@ static bool CreateNonSuspendableThread(void (*threadStart)(void*), void* arg, co ThreadStore::RawGetCurrentThread()->SetGCSpecial(); ThreadStubArguments* pStartContext = (ThreadStubArguments*)argument; + PalSetCurrentThreadName(pStartContext->m_name); auto realStartRoutine = pStartContext->m_pRealStartRoutine; void* realContext = pStartContext->m_pRealContext; delete pStartContext; @@ -567,6 +583,7 @@ static bool CreateNonSuspendableThread(void (*threadStart)(void*), void* arg, co if (!PalStartBackgroundGCThread(threadStub, threadStubArgs)) { + delete[] threadStubArgs->m_name; delete threadStubArgs; return false; } @@ -576,14 +593,13 @@ static bool CreateNonSuspendableThread(void (*threadStart)(void*), void* arg, co bool GCToEEInterface::CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name) { - UNREFERENCED_PARAMETER(name); - if (!is_suspendable) return CreateNonSuspendableThread(threadStart, arg, name); ThreadStubArguments threadStubArgs; threadStubArgs.m_pRealStartRoutine = threadStart; threadStubArgs.m_pRealContext = arg; + threadStubArgs.m_name = name; if (!threadStubArgs.m_ThreadStartedEvent.CreateAutoEventNoThrow(false)) { @@ -608,7 +624,7 @@ bool GCToEEInterface::CreateThread(void (*threadStart)(void*), void* arg, bool i auto realStartRoutine = pStartContext->m_pRealStartRoutine; void* realContext = pStartContext->m_pRealContext; - + PalSetCurrentThreadName(pStartContext->m_name); pStartContext->m_ThreadStartedEvent.Set(); STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY); diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index c2e94a1dc8f38..f5128d505838f 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -1346,3 +1346,13 @@ FCIMPLEND #endif //USE_PORTABLE_HELPERS #endif // !DACCESS_COMPILE + + +EXTERN_C void QCALLTYPE RhSetCurrentThreadName(const TCHAR* name) +{ +#ifdef TARGET_WINDOWS + PalSetCurrentThreadNameW(name); +#else + PalSetCurrentThreadName(name); +#endif +} \ No newline at end of file diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index d30c3cb3aa6af..aac2e811bc807 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -716,6 +716,20 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundWork(_In_ BackgroundCall return st == 0; } +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalSetCurrentThreadName(const char* name) +{ + const int MAX_THREAD_NAME_SIZE = 15; + char name_copy[MAX_THREAD_NAME_SIZE + 1]; + strncpy(name_copy, name, MAX_THREAD_NAME_SIZE); + name_copy[MAX_THREAD_NAME_SIZE] = '\0'; +#ifdef __APPLE__ + pthread_setname_np(name_copy); +#else + pthread_setname_np(pthread_self(), name_copy); +#endif //__APPLE__ + return true; +} + REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) { return PalStartBackgroundWork(callback, pCallbackContext, UInt32_FALSE); diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 86013f7a964d2..64ecc88ffa196 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -883,6 +883,10 @@ REDHAWK_PALEXPORT void REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_opt_ void* p ResumeThread(hThread); } +#define SET_THREAD_DESCRIPTION_UNINITIALIZED (pfnSetThreadDescription)-1 +typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription); +static pfnSetThreadDescription g_pfnSetThreadDescription = SET_THREAD_DESCRIPTION_UNINITIALIZED; + REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundWork(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext, BOOL highPriority) { HANDLE hThread = CreateThread( @@ -906,6 +910,40 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundWork(_In_ BackgroundCall return true; } +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalSetCurrentThreadNameW(const WCHAR* name) +{ + if (g_pfnSetThreadDescription == SET_THREAD_DESCRIPTION_UNINITIALIZED) + { + HMODULE hKernel32 = LoadKernel32dll(); + g_pfnSetThreadDescription = (pfnSetThreadDescription)GetProcAddress(hKernel32, "SetThreadDescription"); + } + if (!g_pfnSetThreadDescription) + { + return false; + } + HANDLE hThread = GetCurrentThread(); + g_pfnSetThreadDescription(hThread, name); + return true; +} + +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalSetCurrentThreadName(const char* name) +{ + size_t len = strlen(name); + wchar_t* threadNameWide = new (nothrow) wchar_t[len + 1]; + if (threadNameWide == nullptr) + { + return false; + } + if (MultiByteToWideChar(CP_UTF8, 0, name, -1, threadNameWide, (int)(len + 1)) == 0) + { + delete[] threadNameWide; + return false; + } + bool ret = PalSetCurrentThreadNameW(threadNameWide); + delete[] threadNameWide; + return ret; +} + REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) { return PalStartBackgroundWork(callback, pCallbackContext, FALSE); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 7f833e613e5c4..ecf2b485100d3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -877,5 +877,13 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe [LibraryImport(RuntimeLibrary)] internal static unsafe partial void RhCpuIdEx(int* cpuInfo, int functionId, int subFunctionId); #endif + +#if TARGET_UNIX + [LibraryImport(RuntimeLibrary, StringMarshalling = StringMarshalling.Utf8)] + internal static partial void RhSetCurrentThreadName(string name); +#else + [LibraryImport(RuntimeLibrary, StringMarshalling = StringMarshalling.Utf16)] + internal static partial void RhSetCurrentThreadName(string name); +#endif } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 7fc526fc8347e..7481f3c96a95b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -198,8 +198,19 @@ public int ManagedThreadId get => _managedThreadId.Id; } - // TODO: Inform the debugger and the profiler - // private void ThreadNameChanged(string? value) {} + // TODO: Support non-current thread + private void ThreadNameChanged(string? value) + { + if (Thread.CurrentThread != this) + { + return; + } + if (value == null) + { + return; + } + RuntimeImports.RhSetCurrentThreadName(value); + } public ThreadPriority Priority { diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 3253593639122..b4cdca7f272b4 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -1395,6 +1395,9 @@ struct SuspendableThreadStubArguments class Thread* Thread; bool HasStarted; CLREvent ThreadStartedEvent; +#ifdef __APPLE__ + const WCHAR* name; +#endif //__APPLE__ }; struct ThreadStubArguments @@ -1404,6 +1407,9 @@ struct ThreadStubArguments HANDLE Thread; bool HasStarted; CLREvent ThreadStartedEvent; +#ifdef __APPLE__ + const WCHAR* name; +#endif //__APPLE__ }; namespace @@ -1422,6 +1428,9 @@ namespace args.ThreadStart = threadStart; args.Thread = nullptr; args.HasStarted = false; +#ifdef __APPLE__ + args.name = name; +#endif //__APPLE__ if (!args.ThreadStartedEvent.CreateAutoEventNoThrow(FALSE)) { return false; @@ -1447,6 +1456,10 @@ namespace SuspendableThreadStubArguments* args = static_cast(argument); assert(args != nullptr); +#ifdef __APPLE__ + SetThreadName(GetCurrentThread(), args->name); +#endif //__APPLE__ + ClrFlsSetThreadType(ThreadType_GC); args->Thread->SetGCSpecial(); STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY); @@ -1504,6 +1517,9 @@ namespace args.Argument = argument; args.ThreadStart = threadStart; args.Thread = INVALID_HANDLE_VALUE; +#ifdef __APPLE__ + args.name = name; +#endif //__APPLE__ if (!args.ThreadStartedEvent.CreateAutoEventNoThrow(FALSE)) { return false; @@ -1514,6 +1530,10 @@ namespace ThreadStubArguments* args = static_cast(argument); assert(args != nullptr); +#ifdef __APPLE__ + SetThreadName(GetCurrentThread(), args->name); +#endif //__APPLE__ + ClrFlsSetThreadType(ThreadType_GC); STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index 0f7b2fc570a39..92ea2b5f399bd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -298,8 +298,6 @@ private void SetCultureOnUnstartedThread(CultureInfo value, bool uiCulture) } } - partial void ThreadNameChanged(string? value); - public CultureInfo CurrentCulture { get diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Mono.cs index 3c3e407272238..5715dd23923f6 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Mono.cs @@ -235,7 +235,7 @@ private void StartCore() [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern void StartInternal(Thread runtimeThread, int stackSize); - partial void ThreadNameChanged(string? value) + private void ThreadNameChanged(string? value) { // TODO: Should only raise the events SetName(this, value);