signal/signal: creates package for monitoring of OS signals
This commit is contained in:
parent
8ab464b4d1
commit
0ae6dec4f3
96
signal.go
96
signal.go
@ -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
101
signal/signal.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user