Uh oh!
There was an error while loading. Please reload this page.
Deduplicate minipal thread ID TLS cache. - #131991
Conversation
Move the cached thread ID into a single minipal compilation unit instead of emitting one TLS slot per translation unit that includes thread.h.
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Tagging subscribers to this area: @JulieLeeMSFT, @VSadov |
There was a problem hiding this comment.
Pull request overview
Deduplicates the TLS-backed cache used by minipal_get_current_thread_id() by moving the cached thread ID out of a function-local TLS and into a single TLS variable defined in a dedicated minipal compilation unit.
Changes:
- Introduces
MINIPAL_THREAD_LOCALand uses it to declare a shared TLS variableminipal_cached_thread_idinthread.h. - Adds
thread.cto defineminipal_cached_thread_id(under the same WASM/reentrancy guards). - Updates minipal’s CMake source list to compile
thread.con Unix hosts.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/native/minipal/thread.h | Switches the cache from a function-local TLS static to an extern TLS variable and adds a TLS macro. |
| src/native/minipal/thread.c | Defines the shared TLS variable so all translation units refer to the same TLS slot. |
| src/native/minipal/CMakeLists.txt | Ensures thread.c is built into minipal on Unix. |
Uh oh!
There was an error while loading. Please reload this page.
EgorBo
commented
Aug 7, 2026
@EgorBot -linux_arm64 --filter "System.Tests.Perf_UInt16.Parse*" |
Uh oh!
There was an error while loading. Please reload this page.
AndyAyersMS
commented
Aug 7, 2026
Uh oh!
There was an error while loading. Please reload this page.
mdh1418
commented
Aug 11, 2026
@EgorBot -linux_arm64 usingBenchmarkDotNet.Attributes;publicclassPerf_Enum{[Benchmark][Arguments(DayOfWeek.Wednesday,"x")]publicstringToString_Format_NonFlags(DayOfWeekvalue,stringformat)=>value.ToString(format);} |
Uh oh!
There was an error while loading. Please reload this page.
Addresses #131991 (comment) Deduplicate the helper functions currently defined with internal linkage in src/native/minipal headers. The affected helpers remain defined as inline in their headers so callers can inline them, but paired .c files now provide one external fallback definition for cases where the compiler emits a call instead. In C, each paired source file does this by including the inline definition and then redeclaring the function with extern. ## C inline linkage A plain C inline definition does not necessarily emit an externally linkable function. At higher optimization levels, the compiler may substitute the header implementation directly at the call site, but at lower optimization levels, or whenever it chooses not to inline, the generated code may call an external symbol. Each paired source file therefore follows this pattern: ``` #include "header.h" extern return_type function(arguments); ``` The header supplies the function body, and the extern redeclaration causes that translation unit to provide the external definition required by non-inlined callers. This preserves access to the inline implementation while avoiding a private `static` copy in every translation unit. ## Out-of-line helpers Based on review feedback, the following helpers are not sufficiently performance-sensitive to justify retaining their implementations in headers: - minipal_getexepath - minipal_get_current_thread_id_no_cache - minipal_set_thread_name Their implementations now live in getexepath.c and thread.c , and their headers contain declarations only. minipal_get_current_thread_id remains inline because its common path is a TLS lookup and branch. It calls the out-of-line uncached implementation only when the TLS cache is empty. Moving minipal_set_thread_name and the uncached thread-ID implementation into thread.c also keeps _GNU_SOURCE source-local. Arbitrary consumers of thread.h no longer compile code requiring GNU-only declarations. ## Executable-path configuration The executable-path implementation uses getauxval(AT_EXECFN) as a Linux fallback when /proc/self/exe cannot be resolved. Availability was previously determined by component-specific generated configuration headers, which were not available to minipal’s source file. Minipal now performs its own getauxval capability check and exposes the result through minipalconfig.h . Because minipal_getexepath has one out-of-line implementation, all callers now use the same capability-tested behavior regardless of optimization level or consumer configuration. ## CPUID linker symbol names The CPUID fallback helpers retain their source-level names, `__cpuid` and `__cpuidex` , to match the corresponding compiler intrinsics. Those names were harmless while the functions were `static` , because each definition had translation-unit-local linkage. Providing external fallback definitions under those names would export reserved double-underscore symbols and could collide with compiler headers or compatibility shims. Assembler-name labels are therefore used to assign minipal-owned linker names: `inline void __cpuid(...) __asm("minipal_cpuid");` `inline void __cpuidex(...) __asm("minipal_cpuidex");` This preserves the existing source-level API while emitting the external symbols as minipal_cpuid and minipal_cpuidex . These labels are separate from the inline assembly inside the function bodies that executes the CPUID instruction. Validation - Built clr+libs+host for Linux x64 Debug. - Verified GCC and Clang C consumers link at -O0 using the external definitions. - Verified optimized consumers can use the inline definitions. - Verified C++ consumers use compatible C-linkage symbols. - Verified the minipal archive provides the expected external helper symbols. - Verified the shipped archives expose minipal_cpuid and minipal_cpuidex rather than strong __cpuid and __cpuidex symbols. - Verified no _SOURCE or _INLINE implementation-control macros remain. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>


Summary
Deduplicate the TLS cache used by
minipal_get_current_thread_id.Fixes#131954.
Root cause
minipal_get_current_thread_idpreviously declared its cached thread ID as a function-localstatic thread_localvariable inthread.h.Because the function has internal linkage, each translation unit using it could emit a separate 8-byte TLS slot. Enabling the in-process crash reporter added another consumer, increasing
libcoreclr.so's TLS footprint enough to exceed glibc's optional static TLS allocation on Linux ARM64.This caused glibc to resolve CoreCLR TLS accesses through
_dl_tlsdesc_dynamicinstead of_dl_tlsdesc_return, adding overhead to allocation, thread-static access, and other common runtime paths.Changes
thread.h.Validation
A test with two independent C and C++ translation units showed: