From 1aa65e6863513b29c1fa4ef02b52b8ad7af10e05 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 26 Oct 2013 16:02:23 -0500 Subject: [PATCH] Correct and improve SIGINT (Ctrl+C) handling. Since the main SIGINT handler is running as a goroutine, the main goroutine must be kept active long enough for it to finish or it will be nuked when the main goroutine exits. This commit makes that happen by slightly modifying how it waits for shutdown. Rather than having the main goroutine only wait for the server to shutdown, there is now a shutdown channel that is used to signal the main goroutine to shutdown for all cases such as a graceful shutdown, a scheduled shutdown, an RPC stop, or a SIGINT. While here, also add a few prints to indicate a SIGINT was received and the shutdown progress. --- btcd.go | 20 ++++++++++++++++++-- signal.go | 5 ++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/btcd.go b/btcd.go index 7c3bb3758..b854f2f23 100644 --- a/btcd.go +++ b/btcd.go @@ -13,7 +13,8 @@ import ( ) var ( - cfg *config + cfg *config + shutdownChannel = make(chan bool) ) // btcdMain is the real main function for btcd. It is necessary to work around @@ -71,6 +72,7 @@ func btcdMain() error { // Ensure the database is sync'd and closed on Ctrl+C. addInterruptHandler(func() { + log.Infof("Gracefully shutting down the database...") db.RollbackClose() }) @@ -83,7 +85,21 @@ func btcdMain() error { } server.Start() - server.WaitForShutdown() + // Monitor for graceful server shutdown and signal the main goroutine + // when done. This is done in a separate goroutine rather than waiting + // directly so the main goroutine can be signaled for shutdown by either + // a graceful shutdown or from the main interrupt handler. This is + // necessary since the main goroutine must be kept running long enough + // for the interrupt handler goroutine to finish. + go func() { + server.WaitForShutdown() + shutdownChannel <- true + }() + + // Wait for shutdown signal from either a graceful server stop or from + // the interrupt handler. + <-shutdownChannel + log.Info("Shutdown complete") return nil } diff --git a/signal.go b/signal.go index 6f8340e7b..438f9c7be 100644 --- a/signal.go +++ b/signal.go @@ -27,10 +27,13 @@ func mainInterruptHandler() { for { select { case <-interruptChannel: + log.Infof("Received SIGINT (Ctrl+C). Shutting down...") for _, callback := range interruptCallbacks { callback() } - os.Exit(0) + + // Signal the main goroutine to shutdown. + shutdownChannel <- true case handler := <-addHandlerChannel: interruptCallbacks = append(interruptCallbacks, handler)