Jul 2, 2020

[Go] goroutine preemption

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?

  1. A dedicated thread M called sysmon will watch every goroutines in every P running longer than 10ms.
  2. Once over 10ms, sysmon will send event signal SIGURG to those goroutines.
  3. 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.