From c0e2f7a7c491116210fa78fc9d730dcfbedaa5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 5 Sep 2026 07:16:27 +0200 Subject: [PATCH 1/2] fix(gc): route the census thread-locals through hot TLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `scripts/check_thread_locals.py` fails on main, taking the `self-test-checkers` job down on every PR: thread-local policy check FAILED: crates/perry-runtime/src/gc/census.rs: 2 raw `thread_local!` block(s), none allowed. `gc::census` is compiled unconditionally (`pub(crate) mod census;`), so its two ungated `thread_local!` blocks are in shipping builds and pay `_tlv_get_addr` per access on Darwin — exactly what #7469's policy exists to prevent. The third block in the file is `#[cfg(test)]` and is out of scope by construction. Converted to `crate::perry_thread_local!`, which is the same syntax and the same `.with()` at every call site — all nine uses of ARMED/SEQ/LABEL/ PASS1_MARKED already go through `.with()`, so nothing else changes. This is the same treatment 12efed122 gave the four other recent declarations; census was missed. Claude-Session: https://claude.ai/code/session_01YPfnmWZmSpSWpmnoXvH8z2 --- crates/perry-runtime/src/gc/census.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/perry-runtime/src/gc/census.rs b/crates/perry-runtime/src/gc/census.rs index 891035533f..aea37815e6 100644 --- a/crates/perry-runtime/src/gc/census.rs +++ b/crates/perry-runtime/src/gc/census.rs @@ -43,7 +43,7 @@ static SIGNAL_PENDING: AtomicBool = AtomicBool::new(false); static SIGNAL_INSTALLED: AtomicBool = AtomicBool::new(false); static MAIN_THREAD: OnceLock = OnceLock::new(); -thread_local! { +crate::perry_thread_local! { static ARMED: Cell = const { Cell::new(false) }; static SEQ: Cell = const { Cell::new(0) }; static LABEL: RefCell<&'static str> = const { RefCell::new("manual") }; @@ -177,7 +177,7 @@ fn census_service_signal() { super::js_gc_collect(); } -thread_local! { +crate::perry_thread_local! { /// Pass-1 snapshot: sorted header addresses that were marked when mark /// propagation finished (see the module docs). static PASS1_MARKED: RefCell>> = const { RefCell::new(None) }; From 4367aade921b5c490536f760feca9314b8f72f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 5 Sep 2026 07:18:50 +0200 Subject: [PATCH 2/2] docs(changelog): add PR 9781 fragment Claude-Session: https://claude.ai/code/session_01YPfnmWZmSpSWpmnoXvH8z2 --- changelog.d/9781-census-hot-tls.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog.d/9781-census-hot-tls.md diff --git a/changelog.d/9781-census-hot-tls.md b/changelog.d/9781-census-hot-tls.md new file mode 100644 index 0000000000..2d4e53bb33 --- /dev/null +++ b/changelog.d/9781-census-hot-tls.md @@ -0,0 +1,10 @@ +**The GC census thread-locals no longer pay `_tlv_get_addr`.** `gc::census` +is compiled unconditionally, so its two ungated `thread_local!` blocks were +in shipping builds and cost an out-of-line libdyld call per access on Darwin +— including the `ARMED` flag that gates whether the census does anything at +all. Both now use `crate::perry_thread_local!`, the same treatment the four +other recent declarations received; every call site already went through +`.with()`, so nothing else changed. + +This also restores the `self-test-checkers` job, whose thread-local policy +ratchet (#7469) had been failing on main and on every open PR.