github.com/searKing/golang/go@v1.2.117/context/signal.go (about) 1 package context 2 3 import ( 4 "context" 5 "os" 6 "os/signal" 7 "sync" 8 9 os_ "github.com/searKing/golang/go/os" 10 ) 11 12 var onlyOneSignalHandler = make(chan struct{}) 13 14 // WithShutdownSignal registered for signals. A context.Context is returned. 15 // If no signals are provided, incoming os_.ShutdownSignals signals will be relayed. 16 // Otherwise, just the provided signals will. 17 // which is done on one of these incoming signals. If a second signal is caught, the program 18 // is terminated with exit code 1. 19 // Only one of Signal should be called, and only can be called once. 20 func WithShutdownSignal(parent context.Context, sig ...os.Signal) context.Context { 21 if len(sig) == 0 { 22 sig = os_.ShutdownSignals 23 } 24 close(onlyOneSignalHandler) // panics when called twice 25 26 var shutdownSignalC chan os.Signal 27 shutdownSignalC = make(chan os.Signal, 2) 28 29 ctx, cancel := context.WithCancel(parent) 30 signal.Notify(shutdownSignalC, sig...) 31 go func() { 32 defer signal.Stop(shutdownSignalC) 33 select { 34 case <-shutdownSignalC: 35 cancel() 36 case <-ctx.Done(): 37 return 38 } 39 40 select { 41 case <-shutdownSignalC: 42 os.Exit(1) // second signal. Exit directly. 43 case <-ctx.Done(): 44 } 45 }() 46 47 return ctx 48 } 49 50 // WithSignal registered for signals. A context.Context is returned. 51 // which is done on one of these incoming signals. 52 // signals can be stopped by stopSignal, context will never be Done() if stopped. 53 // WithSignal returns a copy of the parent context registered for signals. 54 // If the parent's context is already done than d, WithSignal(parent, sig...) is semantically 55 // equivalent to parent. The returned context's Done channel is closed when one of these 56 // incoming signals, when the returned cancel function is called, or when the parent context's 57 // Done channel is closed, whichever happens first. 58 // Canceling this context releases resources associated with it, so code should 59 // call cancel as soon as the operations running in this Context complete. 60 // Deprecated: Use signal.NotifyContext instead since go1.16. 61 func WithSignal(parent context.Context, sig ...os.Signal) (ctx context.Context, cancel context.CancelFunc) { 62 var c chan os.Signal 63 c = make(chan os.Signal, 1) 64 65 ctx, cancel_ := context.WithCancel(parent) 66 67 var once sync.Once 68 cancel = func() { 69 once.Do(func() { 70 // https://groups.google.com/g/golang-nuts/c/pZwdYRGxCIk/m/qpbHxRRPJdUJ 71 // https://groups.google.com/g/golang-nuts/c/bfuwmhbZHYw/m/PNnKk4hGytgJ 72 // The close function should only be used on a channel used to send data, 73 // after all data has been sent. It does not make sense to send more data 74 // after the channel has been closed; if all data has not been sent, the 75 // channel should not be closed. Similarly, it does not make sense to 76 // close the channel twice. 77 // 78 // Note that it is only necessary to close a channel if the receiver is 79 // looking for a close. Closing the channel is a control signal on the 80 // channel indicating that no more data follows. 81 // BUT 82 // Channels persist as long as one reference to them persists, 83 // since without difficult (and in general impossible) analysis 84 // it's hard to prove a second reference won't arise. 85 defer close(c) 86 87 signal.Stop(c) 88 cancel_() 89 }) 90 } 91 92 signal.Notify(c, sig...) 93 go func() { 94 select { 95 case <-c: 96 case <-ctx.Done(): 97 } 98 cancel() 99 }() 100 101 return ctx, cancel 102 }