What happens to the scheduler when a goroutine makes a blocking syscall?
Question 157HardGo 1.22 to 1.25
Before the syscall, the runtime calls entersyscall. The G moves to _Gsyscall, and its P moves to _Psyscall but stays attached to the M. The goal is to make fast syscalls cheap.
- Fast path: the syscall returns quickly.
exitsyscalltakes the same P back and the goroutine continues without any hand-off. - Slow path:
sysmonsees that the P has been in a syscall for more than about 20 µs (one sysmon tick) and has other work to do. It callsretake→handoffp, and the P goes to another M. That M may be idle or newly created. When the syscall eventually returns, the original M tries to get an idle P. If none is free, it puts the G on the global queue and parks itself. - Syscalls known to block (
entersyscallblock) hand the P off immediately.
Consequences:
- Many goroutines blocked in file I/O or cgo can create many OS threads. The default limit is 10,000 threads (
debug.SetMaxThreads), and going past it crashes the program. - Regular files cannot go through epoll on Linux, so file I/O blocks threads.
- cgo calls are handled like syscalls.
More on Goroutines & the Scheduler
- 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?
- Q158What is the netpoller and how does it make network I/O look blocking yet scale?
- Q159What is sysmon and what does it do?
- Q160What does runtime.Gosched do and when (if ever) should you use it?
- Q161What does runtime.LockOSThread do and when is it needed?