On Sun, Dec 8, 2019 at 7:02 AM changkun <euryugas...@gmail.com> wrote:
>
> 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.


It sounds like you are doing a good job of investigating.  I don't
know the answer to your question, but it sounds worth figuring out.  I
would encourage you to keep looking into this.  Be sure to track which
goroutine is seeing the preemption call; perhaps there is some
confusion between different goroutines.  Or, in newstack you could
perhaps call getcallerpc to find out exactly what is calling the
function.

Ian

-- 
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/CAOyqgcUxfYbRBXdL%2Bu5gcZFmha7CNCDi61%2BQ-YXnNKVrHyRNsA%40mail.gmail.com.

Reply via email to