[clr-ios] Thread shows "No Name" because GetObject returns null at CreateThread time
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Problem
When debugging CoreCLR on Apple mobile in Debug with an R2R corelib, some threads show "No Name" in the Threads window because CordbThread::GetObject returns a null managed thread object. The reason is that the managed System.Threading.Thread object is separate from the native thread and is created lazily, only the first time managed code on that thread reads Thread.CurrentThread. It is set up front only for threads created through managed new Thread().Start, including thread-pool workers. The debugger's CreateThread announcement, however, is armed as soon as the native thread first reaches managed code, on both the SetupThread and HasStarted startup paths, and neither of them creates the managed Thread object. As a result, for native-origin threads such as the main thread or natively attached threads, the exposed object is still null when the CreateThread event is processed, GetObject returns null, and the thread has no name. It behaves the same on desktop, where it simply is not noticed because by the time the name is rendered the thread has already run enough managed code to create the object. This scenario just happens to query GetObject while the object is still null.
### Proposed fix
When the managed Thread object is first created lazily and a debugger is attached, proactively notify the debugger so it re-queries the thread, reusing the exact same `Debugger::NameChangeEvent` API that `Thread.Name` already uses. That path calls `g_pDebugInterface->NameChangeEvent(NULL, pThread)` (from `ThreadNative_InformThreadNameChange` in `src/coreclr/vm/comsynchronizable.cpp`), which `Debugger::NameChangeEvent` (`src/coreclr/debug/ee/debugger.cpp`) turns into a `THREAD_NAME_CHANGE` IPC event surfaced to the debugger through `ICorDebugManagedCallback::NameChange`. Concretely, in Thread::GetExposedObject, after the object is created and the ThreadStore lock is released, invoke that same `NameChangeEvent` API when the current thread just created its own exposed object under an attached debugger. This closes the null window regardless of which announcement path fired or which platform is running, and it costs nothing for the common managed-created threads because they already have the object and never enter this path. It also avoids the riskier alternative of eagerly allocating the managed Thread object during thread startup, which would add a managed allocation and lock acquisition at a delicate point in the thread's lifetime.
Before proceeding with a PR, I want to confirm that the affected threads are indeed native-origin threads with no exposed object at announcement time, and that the `NameChangeEvent` API is the right way to make the debugger re-query the thread.
/cc: @thaystg @matouskozak @janvorli
Contributor guide
Assessment
This issue has not been assessed yet.