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  }