Go runtime/g0 schedules the goroutines when:
- system call
- blocking on channel
- sleeping
- waiting on a mutex
- when goroutine needs to grow it's stack size
- goroutines running for more than 10ms is marked as preemptible (the preemption is initiated/monitored by the thread sysmon), and the preemption is done at the function prolog when the goroutine’s stack is increasing if without any above pause criteria
- For those goroutines does not increasing size, using runtime.Gosched() specifically to force the goroutine being preempted.
- In Go 1.14, goroutines will auto-preempt even no stack size changes or any criteria listed above meet.
This can be turned off with $ GODEBUG=asyncpreemptoff=1
How does async preemp work?
- A dedicated thread M called sysmon will watch every goroutines in every P running longer than 10ms.
- Once over 10ms, sysmon will send event signal SIGURG to those goroutines.
- Every P has a gsignal goroutine to handle signals. Once a goroutine runs over 10ms receiving SIGURG will be parked into local queue and gsignal takes over handling the SIGURG.
This completes the preemption.
Why choosing signel SIGURG?
In “Proposal: Non-cooperative goroutine preemption”:- It should be a signal that’s passed-through by debuggers by default.
- It shouldn’t be used internally by libc in mixed Go/C binaries [...].
- It should be a signal that can happen spuriously without consequences.
- We need to deal with platforms without real-time signals [...].
Like thread cancellation point, goroutine can not be stop anywhere in the code. It has to be a safe point.
Go 1.14's async preemption also enables GC to STW by sending SIGURG to all goroutines.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.