From 89a2ed80e9e0fbcf46793d22ab996f3aa55f3784 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 18 Sep 2023 03:19:09 -0500 Subject: [PATCH] main: Support SIGTERM on Win and all unix variants. Go 1.14 added runtime support for handling the windows CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT events by adding a definition for syscall.SIGTERM to windows and converting the events to that signal. It also added definitions for other common signals, including SIGHUP, and treats them as NOOPs on windows. Consequently, this modifies the logic that deals with conditionally handling SIGTERM and SIGHUP to handle them for windows as well as all unix-like operating systems, including newer ones that are supported by Go since the code was originally written. Although the additional signals could probably just be added unconditionally without too much issue now due to the runtime adding stubs to all operating systems the project officially supports, it is safer to be explicit when dealing with syscall definitions since there is no guarantee that they will exist for newly-added operating systems. Stated more plainly, this change means dcrpool will now be shutdown cleanly on more variants of unix as well as when being terminated by windows itself due to various things such as the user logging off, the window terminal being closed, and the system shutting down. It also pulls in the logic from dcrd so that logging is added when a signal is received so it is clear why the process is being shutdown. --- dcrpool.go | 26 +++++--------------------- signal.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ signal_syscall.go | 15 +++++++++++++++ signal_unix.go | 21 --------------------- 4 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 signal.go create mode 100644 signal_syscall.go delete mode 100644 signal_unix.go diff --git a/dcrpool.go b/dcrpool.go index 2d5f01a0..36782393 100644 --- a/dcrpool.go +++ b/dcrpool.go @@ -5,14 +5,12 @@ package main import ( - "context" "errors" "fmt" "math" "net/http" _ "net/http/pprof" "os" - "os/signal" "path/filepath" "runtime" "strings" @@ -23,10 +21,6 @@ import ( "github.com/decred/dcrpool/pool" ) -// signals defines the signals that are handled to do a clean shutdown. -// Conditional compilation is used to also include SIGTERM and SIGHUP on Unix. -var signals = []os.Signal{os.Interrupt} - // newHub returns a new pool hub configured with the provided details that is // ready to connect to a consensus daemon and wallet in the case of publicly // available pools. @@ -115,10 +109,6 @@ func newGUI(cfg *config, hub *pool.Hub) (*gui.GUI, error) { // realMain is the real main function for dcrpool. It is necessary to work // around the fact that deferred functions do not run when os.Exit() is called. func realMain() error { - // Listen for interrupt signals. - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, signals...) - // Load configuration and parse command line. This also initializes // logging and configures it accordingly. appName := filepath.Base(os.Args[0]) @@ -139,8 +129,12 @@ func realMain() error { } }() + // Get a context whose done channel will be closed when a shutdown signal + // has been triggered from an OS signal such as SIGINT (Ctrl+C) or when the + // returned cancel function is manually called. + // // Primary context that controls the entire process. - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := shutdownListener() defer mpLog.Info("Shutdown complete") // Show version and home dir at startup. @@ -179,16 +173,6 @@ func realMain() error { }() } - go func() { - select { - case <-ctx.Done(): - return - - case <-interrupt: - cancel() - } - }() - // Create a hub instance and attempt to perform initial connection and work // acquisition. hub, err := newHub(cfg, db) diff --git a/signal.go b/signal.go new file mode 100644 index 00000000..5de202a4 --- /dev/null +++ b/signal.go @@ -0,0 +1,44 @@ +// Copyright (c) 2015-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" + "os" + "os/signal" +) + +// interruptSignals defines the default signals to catch in order to do a proper +// shutdown. This may be modified during init depending on the platform. +var interruptSignals = []os.Signal{os.Interrupt} + +// shutdownListener returns a context whose done channel will be closed when OS +// signals such as SIGINT (Ctrl+C) are received along with a cancel function +// that may be used to manually close the channel. +func shutdownListener() (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(context.Background()) + go func() { + interruptChannel := make(chan os.Signal, 1) + signal.Notify(interruptChannel, interruptSignals...) + + // Listen for initial shutdown signal and cancel the context or + // fallthrough if the caller manually cancels the context. + select { + case sig := <-interruptChannel: + mpLog.Infof("Received signal (%s). Shutting down...", sig) + cancel() + case <-ctx.Done(): + } + + // Listen for repeated signals and display a message so the user knows + // the shutdown is in progress and the process is not hung. + for { + sig := <-interruptChannel + mpLog.Infof("Received signal (%s). Already shutting down...", sig) + } + }() + + return ctx, cancel +} diff --git a/signal_syscall.go b/signal_syscall.go new file mode 100644 index 00000000..850f3e5a --- /dev/null +++ b/signal_syscall.go @@ -0,0 +1,15 @@ +// 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. +// +//go:build windows || aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris + +package main + +import ( + "syscall" +) + +func init() { + interruptSignals = append(interruptSignals, syscall.SIGTERM, syscall.SIGHUP) +} diff --git a/signal_unix.go b/signal_unix.go deleted file mode 100644 index 98e1ba6d..00000000 --- a/signal_unix.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2016 The btcsuite developers -// Copyright (c) 2021-2022 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris - -package main - -import ( - "os" - "syscall" -) - -func init() { - signals = []os.Signal{ - os.Interrupt, - syscall.SIGTERM, - syscall.SIGHUP, - } -}