What does this program do on Go 1.13 vs Go 1.14+?
Question 153HardGo 1.22 to 1.25
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
runtime.GOMAXPROCS(1)
go func() {
for {
}
}()
time.Sleep(10 * time.Millisecond)
fmt.Println("done")
}
Go 1.13 and earlier: the program hangs forever. The spinning goroutine takes the only P when main goes to sleep. The loop contains no function call, so it has no preemption point. The timer fires, but main never gets a P to run on.
Go 1.14+: it prints done. sysmon sees that the goroutine has run for more than 10 ms and sends it SIGURG. The goroutine is preempted asynchronously, and main gets the P back.
Follow-up: busy loops are still wrong, even though they no longer deadlock. They burn a whole CPU. Use channels, sync.Cond or timers instead. With GODEBUG=asyncpreemptoff=1, the program hangs again.
More on Goroutines & the Scheduler
- Q151What does this print with GOMAXPROCS(1), and why?
- Q152What is preemption in Go? Explain cooperative vs. asynchronous preemption (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?
- Q157What happens to the scheduler when a goroutine makes a blocking syscall?