Skip to content

SYSTEM Cited by 1 source

Go runtime scheduler

The Go runtime scheduler is the userspace M:N scheduler that multiplexes a large number of goroutines onto a smaller number of OS kernel threads. Documented in src/runtime/HACKING.md.

The three core types

Type Role Field of note
g Goroutine m — pointer to the kernel thread currently running it (nil if not scheduled)
m Kernel thread ("machine") incgo at offset 0x118 (fault address in the Cloudflare 2025-10 bug)
p Physical execution context ("processor") Queue of runnable gs; bound to an m when executing

For a goroutine to execute: a free m acquires a free p, which then executes a g. Each g records which m is running it via the g.m back-pointer (nil if not currently scheduled).

sysmon and async preemption

The runtime spawns a dedicated monitoring thread, sysmon, that scans running goroutines and preempts any that have run longer than 10 ms (at time of writing). Preemption is performed by sending SIGURG to the goroutine's m; the signal handler mutates the program counter + stack to synthesise a call to runtime.asyncPreempt. See concepts/async-preemption-go.

Before Go 1.14, scheduling was cooperative — goroutines yielded only at explicit points (runtime.Gosched(), function prologues, I/O). Async preemption widens the instant-of-yield from "call site" to "any instruction boundary".

Stack unwinding requires sp validity

The scheduler / GC / panic-recovery paths traverse goroutine stacks via (*unwinder).next. The unwinder dereferences sp to find the parent frame. If sp is partially modified (mid-function-epilogue on arm64 with a split-ADD stack adjustment), the unwinder reads garbage as a return address. Two failure modes:

Canonical wiki instance: the Cloudflare arm64 compiler bug.

Seen in

Last updated · 200 distilled / 1,178 read