diff --git a/builder/testdata/binary-size.txt b/builder/testdata/binary-size.txt index 024a7d8326..96e0415d9d 100644 --- a/builder/testdata/binary-size.txt +++ b/builder/testdata/binary-size.txt @@ -4,5 +4,5 @@ microbit examples/serial z 2985 439 8 2272 wioterminal examples/pininterrupt z 8314 1790 148 7512 cortex-m-qemu ./testdata/size-corpus z 9923 1301 80 4188 cortex-m-qemu ./testdata/size-corpus 2 17482 862 72 4188 -riscv-qemu ./testdata/size-corpus z 17364 1028 88 16660 -riscv-qemu ./testdata/size-corpus 2 24960 744 80 16652 +riscv-qemu ./testdata/size-corpus z 17396 1028 88 16660 +riscv-qemu ./testdata/size-corpus 2 24976 744 80 16652 diff --git a/src/runtime/gc_stack_cores.go b/src/runtime/gc_stack_cores.go index 66aff00879..c32653fc34 100644 --- a/src/runtime/gc_stack_cores.go +++ b/src/runtime/gc_stack_cores.go @@ -17,7 +17,7 @@ func gcMarkReachable() { // If the other cores haven't started yet (for example, when a GC cycle // happens during init()), we only need to scan the stack of the current // core. - if !secondaryCoresStarted { + if secondaryCoresReady.Load() == 0 { // Scan the stack(s) of the current core. scanCurrentStack() if !task.OnSystemStack() { @@ -98,7 +98,7 @@ func scanstack(sp uintptr) { // Resume the world after a call to gcMarkReachable. func gcResumeWorld() { - if !secondaryCoresStarted { + if secondaryCoresReady.Load() == 0 { // Nothing to do: the world wasn't stopped in gcMarkReachable. return } diff --git a/src/runtime/runtime_rp2.go b/src/runtime/runtime_rp2.go index 9cebcd24f9..b41b034ead 100644 --- a/src/runtime/runtime_rp2.go +++ b/src/runtime/runtime_rp2.go @@ -176,6 +176,10 @@ func runCore1() { // interrupts can still happen while the GC is running. enableSIOFifoInterruptCore1() + // The stop-the-world interrupt must be enabled before the + // secondary core is allowed to enter the scheduler. + waitForSecondaryCoresReady() + // Now start running the scheduler on this core. schedulerLock.Lock() scheduler(false) diff --git a/src/runtime/runtime_rp2040_flashsafe_cores.go b/src/runtime/runtime_rp2040_flashsafe_cores.go index 67e9a4a232..d4adfd6df8 100644 --- a/src/runtime/runtime_rp2040_flashsafe_cores.go +++ b/src/runtime/runtime_rp2040_flashsafe_cores.go @@ -23,9 +23,7 @@ var rp2040FlashSafeState volatile.Register8 // With scheduler=cores it must not be called from an interrupt handler or with // interrupts disabled; the GC stop-the-world path has the same constraint (see #5610). func rp2040EnterFlashSafeSection() (interrupt.State, bool) { - // secondaryCoresStarted is set after startSecondaryCores() returns, so core 1 - // may already run Go code in this window. The GC shares it (see #5610). - multicore := secondaryCoresStarted + multicore := secondaryCoresReady.Load() != 0 if !multicore { return interrupt.Disable(), false } diff --git a/src/runtime/runtime_tinygoriscv_qemu.go b/src/runtime/runtime_tinygoriscv_qemu.go index 049891b81b..05c2a7efbf 100644 --- a/src/runtime/runtime_tinygoriscv_qemu.go +++ b/src/runtime/runtime_tinygoriscv_qemu.go @@ -48,6 +48,10 @@ func main() { // Also enable timer interrupts, for sleepTicksMulticore. riscv.MIE.SetBits(riscv.MIE_MTIE) + // The stop-the-world interrupt must be enabled before the + // secondary core is allowed to enter the scheduler. + waitForSecondaryCoresReady() + // Now start running the scheduler on this core. schedulerLock.Lock() scheduler(false) diff --git a/src/runtime/scheduler_cores.go b/src/runtime/scheduler_cores.go index 294662681b..2f94dba56b 100644 --- a/src/runtime/scheduler_cores.go +++ b/src/runtime/scheduler_cores.go @@ -14,8 +14,13 @@ const hasParallelism = true var mainExited atomic.Uint32 -// True after the secondary cores have started. -var secondaryCoresStarted bool +// Non-zero when secondary cores may enter the scheduler. +var secondaryCoresReady atomic.Uint32 + +func waitForSecondaryCoresReady() { + for secondaryCoresReady.Load() == 0 { + } +} // Which task is running on a given core (or nil if there is no task running on // the core). @@ -190,7 +195,7 @@ func run() { // After package initializers have finished, start all the other cores. startSecondaryCores() - secondaryCoresStarted = true + secondaryCoresReady.Store(1) // Run main.main. callMain() diff --git a/src/runtime/scheduler_notcores.go b/src/runtime/scheduler_notcores.go new file mode 100644 index 0000000000..dcfd12645c --- /dev/null +++ b/src/runtime/scheduler_notcores.go @@ -0,0 +1,5 @@ +//go:build !scheduler.cores + +package runtime + +func waitForSecondaryCoresReady() {}