From 1184a4d8d1a0dbeab6bc113777bb7b72af2574f2 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 22 Jul 2014 08:21:58 -0500 Subject: [PATCH] Prevent hang on shutdown race. This commit prevents a race that could happen on shutdown if a sigint was received between adding the first the interrupt handler and and future handlers. This code is loosely based on initial pull request #154 which was not accepted due to introducing a race of its own. Thanks to @tuxcanfly for the intial pull request and finding the issue. --- signal.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/signal.go b/signal.go index 61f77a61..ac8e7589 100644 --- a/signal.go +++ b/signal.go @@ -24,11 +24,26 @@ func mainInterruptHandler() { // SIGINT (Ctrl+C) is received. var interruptCallbacks []func() + // isShutdown is a flag which is used to indicate whether or not + // the shutdown signal has already been received and hence any future + // attempts to add a new interrupt handler should invoke them + // immediately. + var isShutdown bool + for { select { case <-interruptChannel: + // Ignore more than one shutdown signal. + if isShutdown { + btcdLog.Infof("Received SIGINT (Ctrl+C). " + + "Already shutting down...") + continue + } + + isShutdown = true btcdLog.Infof("Received SIGINT (Ctrl+C). Shutting down...") - // run handlers in LIFO order. + + // Run handlers in LIFO order. for i := range interruptCallbacks { idx := len(interruptCallbacks) - 1 - i callback := interruptCallbacks[idx] @@ -36,9 +51,17 @@ func mainInterruptHandler() { } // Signal the main goroutine to shutdown. - shutdownChannel <- struct{}{} + go func() { + shutdownChannel <- struct{}{} + }() case handler := <-addHandlerChannel: + // The shutdown signal has already been received, so + // just invoke and new handlers immediately. + if isShutdown { + handler() + } + interruptCallbacks = append(interruptCallbacks, handler) } }