diff --git a/cmd/vspd/main.go b/cmd/vspd/main.go index 2790358..bd3cd5c 100644 --- a/cmd/vspd/main.go +++ b/cmd/vspd/main.go @@ -1,11 +1,10 @@ -// Copyright (c) 2020-2022 The Decred developers +// Copyright (c) 2020-2023 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package main import ( - "context" "fmt" "os" "runtime" @@ -82,8 +81,7 @@ func run() int { // Create a context that is cancelled when a shutdown request is received // through an interrupt signal. - shutdownCtx := withShutdownCancel(context.Background()) - go shutdownListener(log) + shutdownCtx := shutdownListener(log) // WaitGroup for services to signal when they have shutdown cleanly. var shutdownWg sync.WaitGroup diff --git a/cmd/vspd/signal.go b/cmd/vspd/signal.go index 44210ec..af67708 100644 --- a/cmd/vspd/signal.go +++ b/cmd/vspd/signal.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2014 The btcsuite developers -// Copyright (c) 2021-2022 The Decred developers +// Copyright (c) 2021-2023 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -17,23 +17,39 @@ import ( // subsystems using the same code paths as when an interrupt signal is received. var shutdownRequestChannel = make(chan struct{}) -// shutdownSignaled is closed whenever shutdown is invoked through an interrupt -// signal or from an JSON-RPC stop request. Any contexts created using -// withShutdownChannel are cancelled when this is closed. -var shutdownSignaled = make(chan struct{}) - // interruptSignals defines the signals that are handled to do a clean shutdown. // Conditional compilation is used to also include SIGTERM and SIGHUP on Unix. var interruptSignals = []os.Signal{os.Interrupt} -// withShutdownCancel creates a copy of a context that is cancelled whenever -// shutdown is invoked through an interrupt signal or from an JSON-RPC stop -// request. -func withShutdownCancel(ctx context.Context) context.Context { - ctx, cancel := context.WithCancel(ctx) +// shutdownListener listens for OS Signals such as SIGINT (Ctrl+C) and shutdown +// requests from requestShutdown. It returns a context that is canceled when +// either signal is received. +func shutdownListener(log slog.Logger) context.Context { + ctx, cancel := context.WithCancel(context.Background()) go func() { - <-shutdownSignaled + interruptChannel := make(chan os.Signal, 1) + signal.Notify(interruptChannel, interruptSignals...) + + // Listen for the initial shutdown signal. + select { + case sig := <-interruptChannel: + log.Infof("Received signal (%s). Shutting down...", sig) + case <-shutdownRequestChannel: + log.Info("Shutdown requested. Shutting down...") + } + cancel() + + // Listen for any more shutdown request and display a message so the + // user knows the shutdown is in progress and the process is not hung. + for { + select { + case sig := <-interruptChannel: + log.Infof("Received signal (%s). Already shutting down...", sig) + case <-shutdownRequestChannel: + log.Info("Shutdown requested. Already shutting down...") + } + } }() return ctx } @@ -43,33 +59,3 @@ func withShutdownCancel(ctx context.Context) context.Context { func requestShutdown() { shutdownRequestChannel <- struct{}{} } - -// shutdownListener listens for shutdown requests and cancels all contexts -// created from withShutdownCancel. This function never returns and is intended -// to be spawned in a new goroutine. -func shutdownListener(log slog.Logger) { - interruptChannel := make(chan os.Signal, 1) - signal.Notify(interruptChannel, interruptSignals...) - - // Listen for the initial shutdown signal - select { - case sig := <-interruptChannel: - log.Infof("Received signal (%s). Shutting down...", sig) - case <-shutdownRequestChannel: - log.Info("Shutdown requested. Shutting down...") - } - - // Cancel all contexts created from withShutdownCancel. - close(shutdownSignaled) - - // Listen for any more shutdown signals and log that shutdown has already - // been signaled. - for { - select { - case sig := <-interruptChannel: - log.Infof("Received signal (%s). Already shutting down...", sig) - case <-shutdownRequestChannel: - log.Info("Shutdown requested. Already shutting down...") - } - } -}