Merge pull request #1392 from cfromknecht/signal-package

Signal/Interrupt package
This commit is contained in:
Olaoluwa Osuntokun 2018-06-28 19:08:51 -07:00 committed by GitHub
commit ff771968b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 142 additions and 123 deletions

37
lnd.go

@ -35,7 +35,6 @@ import (
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
flags "github.com/jessevdk/go-flags" flags "github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
@ -44,6 +43,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcd/wire"
@ -62,7 +62,6 @@ var (
Commit string Commit string
cfg *config cfg *config
shutdownChannel = make(chan struct{})
registeredChains = newChainRegistry() registeredChains = newChainRegistry()
macaroonDatabaseDir string macaroonDatabaseDir string
@ -94,6 +93,8 @@ var (
// defers created in the top-level scope of a main method aren't executed if // defers created in the top-level scope of a main method aren't executed if
// os.Exit() is called. // os.Exit() is called.
func lndMain() error { func lndMain() error {
defer ltndLog.Info("Shutdown complete")
// Load the configuration, and parse any command line options. This // Load the configuration, and parse any command line options. This
// function will also set up logging properly. // function will also set up logging properly.
loadedConfig, err := loadConfig() loadedConfig, err := loadConfig()
@ -500,6 +501,7 @@ func lndMain() error {
return err return err
} }
server.fundingMgr = fundingMgr server.fundingMgr = fundingMgr
defer fundingMgr.Stop()
// Check macaroon authentication if macaroons aren't disabled. // Check macaroon authentication if macaroons aren't disabled.
if macaroonService != nil { if macaroonService != nil {
@ -517,6 +519,7 @@ func lndMain() error {
if err := rpcServer.Start(); err != nil { if err := rpcServer.Start(); err != nil {
return err return err
} }
defer rpcServer.Stop()
grpcServer := grpc.NewServer(serverOpts...) grpcServer := grpc.NewServer(serverOpts...)
lnrpc.RegisterLightningServer(grpcServer, rpcServer) lnrpc.RegisterLightningServer(grpcServer, rpcServer)
@ -574,18 +577,9 @@ func lndMain() error {
ltndLog.Infof("Waiting for chain backend to finish sync, "+ ltndLog.Infof("Waiting for chain backend to finish sync, "+
"start_height=%v", bestHeight) "start_height=%v", bestHeight)
// We'll add an interrupt handler in order to process shutdown
// requests while the chain backend syncs.
addInterruptHandler(func() {
rpcServer.Stop()
fundingMgr.Stop()
})
for { for {
select { if !signal.Alive() {
case <-shutdownChannel:
return nil return nil
default:
} }
synced, _, err := activeChainControl.wallet.IsSynced() synced, _, err := activeChainControl.wallet.IsSynced()
@ -615,10 +609,10 @@ func lndMain() error {
srvrLog.Errorf("unable to start server: %v\n", err) srvrLog.Errorf("unable to start server: %v\n", err)
return err return err
} }
defer server.Stop()
// Now that the server has started, if the autopilot mode is currently // Now that the server has started, if the autopilot mode is currently
// active, then we'll initialize a fresh instance of it and start it. // active, then we'll initialize a fresh instance of it and start it.
var pilot *autopilot.Agent
if cfg.Autopilot.Active { if cfg.Autopilot.Active {
pilot, err := initAutoPilot(server, cfg.Autopilot) pilot, err := initAutoPilot(server, cfg.Autopilot)
if err != nil { if err != nil {
@ -631,21 +625,12 @@ func lndMain() error {
err) err)
return err return err
} }
defer pilot.Stop()
} }
addInterruptHandler(func() {
rpcServer.Stop()
fundingMgr.Stop()
if pilot != nil {
pilot.Stop()
}
server.Stop()
})
// Wait for shutdown signal from either a graceful server stop or from // Wait for shutdown signal from either a graceful server stop or from
// the interrupt handler. // the interrupt handler.
<-shutdownChannel <-signal.ShutdownChannel()
ltndLog.Info("Shutdown complete")
return nil return nil
} }
@ -1036,7 +1021,7 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []net.Addr,
// Don't leave the file open in case the new wallet // Don't leave the file open in case the new wallet
// could not be created for whatever reason. // could not be created for whatever reason.
if err := loader.UnloadWallet(); err != nil { if err := loader.UnloadWallet(); err != nil {
ltndLog.Errorf("Could not unload new " + ltndLog.Errorf("Could not unload new "+
"wallet: %v", err) "wallet: %v", err)
} }
return nil, err return nil, err
@ -1061,7 +1046,7 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []net.Addr,
} }
return walletInitParams, nil return walletInitParams, nil
case <-shutdownChannel: case <-signal.ShutdownChannel():
return nil, fmt.Errorf("shutting down") return nil, fmt.Errorf("shutting down")
} }
} }

2
log.go

@ -20,6 +20,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/signal"
"github.com/roasbeef/btcd/connmgr" "github.com/roasbeef/btcd/connmgr"
) )
@ -89,6 +90,7 @@ func init() {
autopilot.UseLogger(atplLog) autopilot.UseLogger(atplLog)
contractcourt.UseLogger(cnctLog) contractcourt.UseLogger(cnctLog)
sphinx.UseLogger(sphxLog) sphinx.UseLogger(sphxLog)
signal.UseLogger(ltndLog)
} }
// subsystemLoggers maps each subsystem identifier to its associated logger. // subsystemLoggers maps each subsystem identifier to its associated logger.

@ -26,6 +26,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
"github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/blockchain"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
@ -3416,7 +3417,7 @@ func (r *rpcServer) GetNetworkInfo(ctx context.Context,
func (r *rpcServer) StopDaemon(ctx context.Context, func (r *rpcServer) StopDaemon(ctx context.Context,
_ *lnrpc.StopRequest) (*lnrpc.StopResponse, error) { _ *lnrpc.StopRequest) (*lnrpc.StopResponse, error) {
shutdownRequestChannel <- struct{}{} signal.RequestShutdown()
return &lnrpc.StopResponse{}, nil return &lnrpc.StopResponse{}, nil
} }

@ -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
}

26
signal/log.go Normal file

@ -0,0 +1,26 @@
package signal
import "github.com/btcsuite/btclog"
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
DisableLog()
}
// DisableLog disables all library log output. Logging output is disabled
// by default until UseLogger is called.
func DisableLog() {
log = btclog.Disabled
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}

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
}