vspd: Simplify shutdown listener.

The existing signal.go was inherited directly from dcrwallet, and was
slightly more flexible/complicated than required for vspd. This brings
the code more inline with the dcrd implementation which can be
instantiated in one line and with only one parameter.
This commit is contained in:
jholdstock 2023-08-22 11:29:36 +01:00 committed by Jamie Holdstock
parent 350a3f5bb2
commit 8b50fe619a
2 changed files with 30 additions and 46 deletions

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package main package main
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
@ -82,8 +81,7 @@ func run() int {
// Create a context that is cancelled when a shutdown request is received // Create a context that is cancelled when a shutdown request is received
// through an interrupt signal. // through an interrupt signal.
shutdownCtx := withShutdownCancel(context.Background()) shutdownCtx := shutdownListener(log)
go shutdownListener(log)
// WaitGroup for services to signal when they have shutdown cleanly. // WaitGroup for services to signal when they have shutdown cleanly.
var shutdownWg sync.WaitGroup var shutdownWg sync.WaitGroup

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2014 The btcsuite developers // 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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // 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. // subsystems using the same code paths as when an interrupt signal is received.
var shutdownRequestChannel = make(chan struct{}) 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. // interruptSignals defines the signals that are handled to do a clean shutdown.
// Conditional compilation is used to also include SIGTERM and SIGHUP on Unix. // Conditional compilation is used to also include SIGTERM and SIGHUP on Unix.
var interruptSignals = []os.Signal{os.Interrupt} var interruptSignals = []os.Signal{os.Interrupt}
// withShutdownCancel creates a copy of a context that is cancelled whenever // shutdownListener listens for OS Signals such as SIGINT (Ctrl+C) and shutdown
// shutdown is invoked through an interrupt signal or from an JSON-RPC stop // requests from requestShutdown. It returns a context that is canceled when
// request. // either signal is received.
func withShutdownCancel(ctx context.Context) context.Context { func shutdownListener(log slog.Logger) context.Context {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(context.Background())
go func() { 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() 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 return ctx
} }
@ -43,33 +59,3 @@ func withShutdownCancel(ctx context.Context) context.Context {
func requestShutdown() { func requestShutdown() {
shutdownRequestChannel <- struct{}{} 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...")
}
}
}