From 7a9c649268ffffad8bcfad105178de56b45ce039 Mon Sep 17 00:00:00 2001 From: Konstantin Sharlaimov Date: Tue, 14 Jul 2026 20:31:48 +0200 Subject: [PATCH] fix(gc): pause all cores before scanning stack and globals. In gcMarkReachable, busy-wait for other cores to enter the interrupt handler and pause before scanning the GC core's stack or globals. Prevents data race where a running core relocates heap references to globals during mark phase. --- src/runtime/gc_stack_cores.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/runtime/gc_stack_cores.go b/src/runtime/gc_stack_cores.go index 79edf6b65..9100109a2 100644 --- a/src/runtime/gc_stack_cores.go +++ b/src/runtime/gc_stack_cores.go @@ -43,6 +43,12 @@ func gcMarkReachable() { gcPauseCore(i) } + // Busy-wait until all the other cores are ready. + for gcScanState.Load() != numCPU { + spinLoopWait() + } + gcScanState.Store(0) + // Scan the stack(s) of the current core. scanCurrentStack() if !task.OnSystemStack() { @@ -53,13 +59,6 @@ func gcMarkReachable() { // Scan globals. findGlobals(markRoots) - // Busy-wait until all the other cores are ready. They certainly should be, - // after the scanning we did above. - for gcScanState.Load() != numCPU { - spinLoopWait() - } - gcScanState.Store(0) - // Signal each core in turn that they can scan the stack. for i := uint32(0); i < numCPU; i++ { if i == core {