What is preemption in Go? Explain cooperative vs. asynchronous preemption (Go 1.14).
Question 152HardGo 1.22 to 1.25
Before Go 1.14, preemption was cooperative. sysmon marked a goroutine that had run for more than about 10 ms by setting its stack guard to stackPreempt. The goroutine noticed the mark only at a function prologue during its stack check. A tight loop with no function calls, such as for {} or pure arithmetic, never reached that check. It could hold a P forever and stall GC stop-the-world phases, which could freeze the whole program.
Go 1.14 added asynchronous preemption. sysmon or the GC sends the thread a signal (SIGURG on Unix, thread suspension on Windows). The signal handler checks that the goroutine is at an async safe point. If it is, the handler injects a call to asyncPreempt, which saves all registers and yields.
Gotchas:
- Code running in cgo, in assembly marked unsafe for preemption, or in some runtime sections still cannot be preempted.
- Syscalls can be interrupted by
EINTR. Go's standard library retries them, but your own raw syscalls or cgo code may need to handle it. - You can disable async preemption with
GODEBUG=asyncpreemptoff=1to debug.
More on Goroutines & the Scheduler
- Q150How does work stealing work, and in what order does a P look for work?
- Q151What does this print with GOMAXPROCS(1), and why?
- Q153What does this program do on Go 1.13 vs Go 1.14+?
- Q154How does a goroutine's stack grow? What changed from segmented to contiguous stacks?
- Q155What is GOMAXPROCS, what is its default, and what changed in Go 1.25 for containers?
- Q156Should you set GOMAXPROCS higher than the number of CPUs for I/O-bound workloads?