github.com/gogf/gf/v2@v2.7.4/os/gproc/gproc_signal.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gproc 8 9 import ( 10 "context" 11 "github.com/gogf/gf/v2/internal/intlog" 12 "github.com/gogf/gf/v2/util/gutil" 13 "os" 14 "os/signal" 15 "sync" 16 "syscall" 17 ) 18 19 // SigHandler defines a function type for signal handling. 20 type SigHandler func(sig os.Signal) 21 22 var ( 23 // Use internal variable to guarantee concurrent safety 24 // when multiple Listen happen. 25 listenOnce = sync.Once{} 26 waitChan = make(chan struct{}) 27 signalChan = make(chan os.Signal, 1) 28 signalHandlerMu sync.Mutex 29 signalHandlerMap = make(map[os.Signal][]SigHandler) 30 shutdownSignalMap = map[os.Signal]struct{}{ 31 syscall.SIGINT: {}, 32 syscall.SIGQUIT: {}, 33 syscall.SIGKILL: {}, 34 syscall.SIGTERM: {}, 35 syscall.SIGABRT: {}, 36 } 37 ) 38 39 func init() { 40 for sig := range shutdownSignalMap { 41 signalHandlerMap[sig] = make([]SigHandler, 0) 42 } 43 } 44 45 // AddSigHandler adds custom signal handler for custom one or more signals. 46 func AddSigHandler(handler SigHandler, signals ...os.Signal) { 47 signalHandlerMu.Lock() 48 defer signalHandlerMu.Unlock() 49 for _, sig := range signals { 50 signalHandlerMap[sig] = append(signalHandlerMap[sig], handler) 51 } 52 notifySignals() 53 } 54 55 // AddSigHandlerShutdown adds custom signal handler for shutdown signals: 56 // syscall.SIGINT, 57 // syscall.SIGQUIT, 58 // syscall.SIGKILL, 59 // syscall.SIGTERM, 60 // syscall.SIGABRT. 61 func AddSigHandlerShutdown(handler ...SigHandler) { 62 signalHandlerMu.Lock() 63 defer signalHandlerMu.Unlock() 64 for _, h := range handler { 65 for sig := range shutdownSignalMap { 66 signalHandlerMap[sig] = append(signalHandlerMap[sig], h) 67 } 68 } 69 notifySignals() 70 } 71 72 // Listen blocks and does signal listening and handling. 73 func Listen() { 74 listenOnce.Do(func() { 75 go listen() 76 }) 77 78 <-waitChan 79 } 80 81 func listen() { 82 defer close(waitChan) 83 84 var ( 85 ctx = context.Background() 86 wg = sync.WaitGroup{} 87 sig os.Signal 88 ) 89 for { 90 sig = <-signalChan 91 intlog.Printf(ctx, `signal received: %s`, sig.String()) 92 if handlers := getHandlersBySignal(sig); len(handlers) > 0 { 93 for _, handler := range handlers { 94 wg.Add(1) 95 var ( 96 currentHandler = handler 97 currentSig = sig 98 ) 99 gutil.TryCatch(ctx, func(ctx context.Context) { 100 defer wg.Done() 101 currentHandler(currentSig) 102 }, func(ctx context.Context, exception error) { 103 intlog.Errorf(ctx, `execute signal handler failed: %+v`, exception) 104 }) 105 } 106 } 107 // If it is shutdown signal, it exits this signal listening. 108 if _, ok := shutdownSignalMap[sig]; ok { 109 intlog.Printf( 110 ctx, 111 `receive shutdown signal "%s", waiting all signal handler done`, 112 sig.String(), 113 ) 114 // Wait until signal handlers done. 115 wg.Wait() 116 intlog.Print(ctx, `all signal handler done, exit process`) 117 return 118 } 119 } 120 } 121 122 func notifySignals() { 123 var signals = make([]os.Signal, 0) 124 for s := range signalHandlerMap { 125 signals = append(signals, s) 126 } 127 signal.Notify(signalChan, signals...) 128 } 129 130 func getHandlersBySignal(sig os.Signal) []SigHandler { 131 signalHandlerMu.Lock() 132 defer signalHandlerMu.Unlock() 133 return signalHandlerMap[sig] 134 }