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.

Reply via email to