捕获SIGINT和SIGTERM时,Go进程过早死亡

捕获SIGINT和SIGTERM时,Go进程过早死亡

问题描述:

I have a fairly straight forward signal handling and cleanup process:

func signalHandler(shutdown func() error) {
    // Make signal channel and register notifiers for Interupt and Terminate
    sigchan := make(chan os.Signal, 1)
    signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)

    // Block until we receive a signal on the channel
    <-sigchan

    // Shutdown now that we've received the signal
    if err := shutdown(); err != nil {
        msg := fmt.Sprintf("shutdown error: %s", err.Error())
        log.Fatal(msg)
    }

    log.Println("shutdown procedure complete") 

    // Make a clean exit
    os.Exit(0)
}

This hander is running a go routine from the server:

func (s *Server) Run() error {
    go signalHandler(s.Shutdown) 
    ...

    <-s.done // block until done 
    log.Println("done running!") 
    return nil 
}

func (s *Server) Shutdown() error {
    s.done <- true 
    log.Println("all done here!") 
    return nil 
}

The problem is that the process exits before the "all done here!" can be printed to the log, and sometimes "done running" prints and other times it doesn't. It seems like there is an os.Exit() call somewhere else (possibly in one of the libraries I'm using?). I do need to clean up connections and delete temporary files etc, and they're not getting cleaned up properly.

Does anyone know how I diagnose where the process is exiting?

Update: I'm also not seeing "shutdown procedure complete" with the addition of that log statement. It appears that the process is terminating before this function is over.

The issue is that the call to Run() is the last real op in main(). As soon as Run() returns, main() returns, and when main() returns, the program exits. I generally put signal handling in main() itself, rather than in any goroutine, to ensure that main() does not return before I'm done handling post-signal shutdown procedures.

(From the comments, reposting as an answer now that it's sorted.)