github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/signal.go (about)

     1  // Copyright (c) 2013-2014 The btcsuite developers
     2  // Copyright (c) 2016 The Dash developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package main
     7  
     8  import (
     9  	"os"
    10  	"os/signal"
    11  )
    12  
    13  // interruptChannel is used to receive SIGINT (Ctrl+C) signals.
    14  var interruptChannel chan os.Signal
    15  
    16  // addHandlerChannel is used to add an interrupt handler to the list of handlers
    17  // to be invoked on SIGINT (Ctrl+C) signals.
    18  var addHandlerChannel = make(chan func())
    19  
    20  // signals defines the default signals to catch in order to do a proper
    21  // shutdown.
    22  var signals = []os.Signal{os.Interrupt}
    23  
    24  // mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the
    25  // interruptChannel and invokes the registered interruptCallbacks accordingly.
    26  // It also listens for callback registration.  It must be run as a goroutine.
    27  func mainInterruptHandler() {
    28  	// interruptCallbacks is a list of callbacks to invoke when a
    29  	// SIGINT (Ctrl+C) is received.
    30  	var interruptCallbacks []func()
    31  
    32  	// isShutdown is a flag which is used to indicate whether or not
    33  	// the shutdown signal has already been received and hence any future
    34  	// attempts to add a new interrupt handler should invoke them
    35  	// immediately.
    36  	var isShutdown bool
    37  
    38  	for {
    39  		select {
    40  		case sig := <-interruptChannel:
    41  			// Ignore more than one shutdown signal.
    42  			if isShutdown {
    43  				btcdLog.Infof("Received signal (%s).  "+
    44  					"Already shutting down...", sig)
    45  				continue
    46  			}
    47  
    48  			isShutdown = true
    49  			btcdLog.Infof("Received signal (%s).  Shutting down...",
    50  				sig)
    51  
    52  			// Run handlers in LIFO order.
    53  			for i := range interruptCallbacks {
    54  				idx := len(interruptCallbacks) - 1 - i
    55  				callback := interruptCallbacks[idx]
    56  				callback()
    57  			}
    58  
    59  			// Signal the main goroutine to shutdown.
    60  			go func() {
    61  				shutdownChannel <- struct{}{}
    62  			}()
    63  
    64  		case handler := <-addHandlerChannel:
    65  			// The shutdown signal has already been received, so
    66  			// just invoke and new handlers immediately.
    67  			if isShutdown {
    68  				handler()
    69  			}
    70  
    71  			interruptCallbacks = append(interruptCallbacks, handler)
    72  		}
    73  	}
    74  }
    75  
    76  // addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) is
    77  // received.
    78  func addInterruptHandler(handler func()) {
    79  	// Create the channel and start the main interrupt handler which invokes
    80  	// all other callbacks and exits if not already done.
    81  	if interruptChannel == nil {
    82  		interruptChannel = make(chan os.Signal, 1)
    83  		signal.Notify(interruptChannel, signals...)
    84  		go mainInterruptHandler()
    85  	}
    86  
    87  	addHandlerChannel <- handler
    88  }