github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf.
     6  
     7  package gproc
     8  
     9  import (
    10  	"context"
    11  	"github.com/wangyougui/gf/v2/internal/intlog"
    12  	"github.com/wangyougui/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  }