_signal.go_ raw

   1  package wallet
   2  
   3  import (
   4  	"os"
   5  	"os/signal"
   6  
   7  	cl "github.com/p9c/p9/pkg/util/cl"
   8  )
   9  
  10  
  11  // interruptChannel is used to receive SIGINT (Ctrl+C) signals.
  12  var interruptChannel chan os.Signal
  13  
  14  
  15  // addHandlerChannel is used to add an interrupt handler to the list of handlers
  16  
  17  // to be invoked on SIGINT (Ctrl+C) signals.
  18  var addHandlerChannel = make(chan func())
  19  
  20  
  21  // interruptHandlersDone is closed after all interrupt handlers run the first
  22  
  23  // time an interrupt is signaled.
  24  var interruptHandlersDone = make(qu.C)
  25  
  26  var simulateInterruptChannel = make(qu.C, 1)
  27  
  28  
  29  // signals defines the signals that are handled to do a clean shutdown.
  30  
  31  // Conditional compilation is used to also include SIGTERM on Unix.
  32  var signals = []os.Signal{os.Interrupt}
  33  
  34  
  35  // simulateInterrupt requests invoking the clean termination process by an
  36  
  37  // internal component instead of a SIGINT.
  38  func simulateInterrupt(	) {
  39  
  40  	select {
  41  	case simulateInterruptChannel <- struct{}{}:
  42  	default:
  43  	}
  44  }
  45  
  46  
  47  // mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the
  48  
  49  // interruptChannel and invokes the registered interruptCallbacks accordingly.
  50  
  51  // It also listens for callback registration.  It must be run as a goroutine.
  52  func mainInterruptHandler(	) {
  53  
  54  
  55  	// interruptCallbacks is a list of callbacks to invoke when a
  56  
  57  	// SIGINT (Ctrl+C) is received.
  58  	var interruptCallbacks []func()
  59  	invokeCallbacks := func() {
  60  
  61  
  62  		// run handlers in LIFO order.
  63  		for i := range interruptCallbacks {
  64  			idx := len(interruptCallbacks) - 1 - i
  65  			interruptCallbacks[idx]()
  66  		}
  67  		close(interruptHandlersDone)
  68  	}
  69  
  70  	for {
  71  		select {
  72  		case sig := <-interruptChannel:
  73  			log <- cl.Infof{
  74  				"received signal (%s) - shutting down...", sig,
  75  			}
  76  			_ = sig
  77  			invokeCallbacks()
  78  			return
  79  		case <-simulateInterruptChannel:
  80  			log <- cl.Inf(
  81  				"received shutdown request - shutting down...",
  82  			)
  83  			invokeCallbacks()
  84  			return
  85  
  86  		case handler := <-addHandlerChannel:
  87  			interruptCallbacks = append(interruptCallbacks, handler)
  88  		}
  89  	}
  90  }
  91  
  92  
  93  // addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) is
  94  
  95  // received.
  96  func addInterruptHandler(	handler func()) {
  97  
  98  
  99  	// Create the channel and start the main interrupt handler which invokes
 100  
 101  	// all other callbacks and exits if not already done.
 102  	if interruptChannel == nil {
 103  		interruptChannel = make(chan os.Signal, 1)
 104  		signal.Notify(interruptChannel, signals...)
 105  		go mainInterruptHandler()
 106  	}
 107  
 108  	addHandlerChannel <- handler
 109  }
 110