As we all know that a program is interrupted by a signal and entering kernel space then switch to a userspace signal handler. After the signal handler is accomplished, it will be reentering the kernel space then switch back to where was interrupted.
I am recently reading the newly implemented async preemption in go 1.14, which uses the OS signal to interrupt a "non-preemptive" user goroutine. I am debugging very simple program: package main import ( "runtime" "time" ) func tightloop() { for { } } func main() { runtime.GOMAXPROCS(1) go tightloop() time.Sleep(time.Millisecond) println("OK") runtime.Gosched() } In Go 1.14, when a preempt signal arrives, the `tightloop` will be interrupted by the OS and entering the pre-configured signal handler `runtimeĀ·sigtramp`: TEXT runtimeĀ·sigtramp(SB),NOSPLIT,$72 MOVQ DX, ctx-56(SP) MOVQ SI, info-64(SP) MOVQ DI, signum-72(SP) MOVQ $runtimeĀ·sigtrampgo(SB), AX CALL AX RET which `sigtrampgo` eventually calls the `sighandler`. //go:nosplit //go:nowritebarrierrec func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { (...) setg(g.m.gsignal) (...) sighandler(sig, info, ctx, g) setg(g) (...) } As far as I read the `sighandler` function, it calls `doSigPreempt` and modifies the `ctx` that passed from system kernel, and sets the `rip` to the prologue of `runtime.asyncPreempt`. //go:nowritebarrierrec func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { _g_ := getg() c := &sigctxt{info, ctxt} (...) if sig == sigPreempt { doSigPreempt(gp, c) } } func doSigPreempt(gp *g, ctxt *sigctxt) { if canPreempt { // here modifies the rip and rsp ctxt.pushCall(funcPC(asyncPreempt)) } (...) } However, I noticed that the asyncPreempt is not immediately executed when the signal handler is complete, instead: 1. `morestack` or `morestack_noctxt` is called after `sighandler` is **returned** (not entering either the epilogue or prologue), which calls `newstack` and check checks the preempt flag and entering schedule loop and therefore schedules the main goroutine to finish the async preemption. 2. the `OK` outputs before executing `asyncPreempt` Here are my inserted print logs in runtime: mstart1 call schedule() enter schedule() park_m call schedule() enter schedule() mstart1 call schedule() enter schedule() mstart1 call schedule() enter schedule() park_m call schedule() enter schedule() park_m call schedule() enter schedule() park_m call schedule() enter schedule() mstart1 call schedule() enter schedule() park_m call schedule() enter schedule() rip: 17149264 eip: 824634034136 before pushCall asyncPreempt after pushCall asyncPreempt rip: 17124704 eip: 824634034128 // rip points to asyncPreempt calling newstack: m0, g0 // how could newstack is called? newstack call gopreempt_m gopreempt_m call goschedImpl goschedImpl call schedule() enter schedule() OK gosched_m call goschedImpl goschedImpl call schedule() enter schedule() asyncPreempt2 asyncPreempt2 asyncPreempt2 asyncPreempt2 preemptPark gopreempt_m call goschedImpl goschedImpl call schedule() enter schedule() while I checked the dumped assembly code, there is no stack split check in neither `asyncPreempt` or `sigtramp`. Sorry for the long story, my questions are: - When, who, and how runtime calls the `morestack` after `sighandler`? What did I miss? - Does modifying `ctx` changes program jumps to the modified `rip` instruction after finishing the signal handler? Thank you very much for reading the question and thanks to the go team building such a brilliant feature. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/c221f5d3-531d-41b3-9b44-7e14e96dc590%40googlegroups.com.