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