signal/signal: creates package for monitoring of OS signals

This commit is contained in:
Conner Fromknecht 2018-06-14 20:15:19 -07:00
parent 8ab464b4d1
commit 0ae6dec4f3
No known key found for this signature in database
GPG Key ID: 39DE78FBE6ACB0EF
2 changed files with 101 additions and 96 deletions

View File

@ -1,96 +0,0 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/signal.go
// Copyright (C) 2015-2017 The Lightning Network Developers
package main
import (
"os"
"os/signal"
)
// interruptChannel is used to receive SIGINT (Ctrl+C) signals.
var interruptChannel chan os.Signal
// shutdownRequestChannel is used to request the daemon to shutdown gracefully,
// similar to when receiving SIGINT.
var shutdownRequestChannel = make(chan struct{})
// addHandlerChannel is used to add an interrupt handler to the list of handlers
// to be invoked on SIGINT (Ctrl+C) signals and shutdown.
var addHandlerChannel = make(chan func())
// mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the
// interruptChannel and shutdown requests on the shutdownRequestChannel, and
// invokes the registered interruptCallbacks accordingly. It also listens for
// callback registration.
// It must be run as a goroutine.
func mainInterruptHandler() {
// interruptCallbacks is a list of callbacks to invoke when a
// SIGINT (Ctrl+C) or a shutdown request 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
// shutdown invokes the registered interrupt handlers, then signals the
// shutdownChannel.
shutdown := func() {
// Ignore more than one shutdown signal.
if isShutdown {
ltndLog.Infof("Already shutting down...")
return
}
isShutdown = true
ltndLog.Infof("Shutting down...")
// Execute the interrupt callbacks in FIFO order.
for _, callback := range interruptCallbacks {
callback()
}
// Signal the main goroutine to shutdown.
go func() {
shutdownChannel <- struct{}{}
}()
}
for {
select {
case <-interruptChannel:
ltndLog.Infof("Received SIGINT (Ctrl+C).")
shutdown()
case <-shutdownRequestChannel:
ltndLog.Infof("Received shutdown request.")
shutdown()
case handler := <-addHandlerChannel:
// The shutdown signal has already been received, so
// just invoke any new handlers immediately.
if isShutdown {
handler()
}
interruptCallbacks = append(interruptCallbacks, handler)
}
}
}
// addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) or a
// shutdown request is received.
func addInterruptHandler(handler func()) {
// Create the channel and start the main interrupt handler which invokes
// all other callbacks and exits if not already done.
if interruptChannel == nil {
interruptChannel = make(chan os.Signal, 1)
signal.Notify(interruptChannel, os.Interrupt)
go mainInterruptHandler()
}
addHandlerChannel <- handler
}

101
signal/signal.go Normal file
View File

@ -0,0 +1,101 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/signal.go
// Copyright (C) 2015-2017 The Lightning Network Developers
package signal
import (
"os"
"os/signal"
)
var (
// interruptChannel is used to receive SIGINT (Ctrl+C) signals.
interruptChannel = make(chan os.Signal, 1)
// shutdownRequestChannel is used to request the daemon to shutdown
// gracefully, similar to when receiving SIGINT.
shutdownRequestChannel = make(chan struct{})
// quit is closed when instructing the main interrupt handler to exit.
quit = make(chan struct{})
// shutdownChannel is closed once the main interrupt handler exits.
shutdownChannel = make(chan struct{})
)
func init() {
signal.Notify(interruptChannel, os.Interrupt)
go mainInterruptHandler()
}
// mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the
// interruptChannel and shutdown requests on the shutdownRequestChannel, and
// invokes the registered interruptCallbacks accordingly. It also listens for
// callback registration.
// It must be run as a goroutine.
func mainInterruptHandler() {
// 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
// shutdown invokes the registered interrupt handlers, then signals the
// shutdownChannel.
shutdown := func() {
// Ignore more than one shutdown signal.
if isShutdown {
log.Infof("Already shutting down...")
return
}
isShutdown = true
log.Infof("Shutting down...")
// Signal the main interrupt handler to exit, and stop accept
// post-facto requests.
close(quit)
}
for {
select {
case <-interruptChannel:
log.Infof("Received SIGINT (Ctrl+C).")
shutdown()
case <-shutdownRequestChannel:
log.Infof("Received shutdown request.")
shutdown()
case <-quit:
log.Infof("Gracefully shutting down.")
close(shutdownChannel)
return
}
}
}
// Alive returns true if the main interrupt handler has not been killed.
func Alive() bool {
select {
case <-quit:
return false
default:
return true
}
}
// RequestShutdown initiates a graceful shutdown from the application.
func RequestShutdown() {
select {
case shutdownRequestChannel <- struct{}{}:
case <-quit:
}
}
// ShutdownChannel returns the channel that will be closed once the main
// interrupt handler has exited.
func ShutdownChannel() <-chan struct{} {
return shutdownChannel
}