Skip to content

Commit

Permalink
main: Support SIGTERM on Win and all unix variants.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davecgh authored and jholdstock committed Sep 18, 2023
1 parent 5bb1641 commit 89a2ed8
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 42 deletions.
26 changes: 5 additions & 21 deletions dcrpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
package main

import (
"context"
"errors"
"fmt"
"math"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
Expand All @@ -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.
Expand Down Expand Up @@ -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])
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
44 changes: 44 additions & 0 deletions signal.go
Original file line number Diff line number Diff line change
@@ -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
}
15 changes: 15 additions & 0 deletions signal_syscall.go
Original file line number Diff line number Diff line change
@@ -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)
}
21 changes: 0 additions & 21 deletions signal_unix.go

This file was deleted.

0 comments on commit 89a2ed8

Please sign in to comment.