...

Source file src/os/signal/signal.go

Documentation: os/signal

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package signal
     6  
     7  import (
     8  	"context"
     9  	"os"
    10  	"sync"
    11  )
    12  
    13  var handlers struct {
    14  	sync.Mutex
    15  	// Map a channel to the signals that should be sent to it.
    16  	m map[chan<- os.Signal]*handler
    17  	// Map a signal to the number of channels receiving it.
    18  	ref [numSig]int64
    19  	// Map channels to signals while the channel is being stopped.
    20  	// Not a map because entries live here only very briefly.
    21  	// We need a separate container because we need m to correspond to ref
    22  	// at all times, and we also need to keep track of the *handler
    23  	// value for a channel being stopped. See the Stop function.
    24  	stopping []stopping
    25  }
    26  
    27  type stopping struct {
    28  	c chan<- os.Signal
    29  	h *handler
    30  }
    31  
    32  type handler struct {
    33  	mask [(numSig + 31) / 32]uint32
    34  }
    35  
    36  func (h *handler) want(sig int) bool {
    37  	return (h.mask[sig/32]>>uint(sig&31))&1 != 0
    38  }
    39  
    40  func (h *handler) set(sig int) {
    41  	h.mask[sig/32] |= 1 << uint(sig&31)
    42  }
    43  
    44  func (h *handler) clear(sig int) {
    45  	h.mask[sig/32] &^= 1 << uint(sig&31)
    46  }
    47  
    48  // Stop relaying the signals, sigs, to any channels previously registered to
    49  // receive them and either reset the signal handlers to their original values
    50  // (action=disableSignal) or ignore the signals (action=ignoreSignal).
    51  func cancel(sigs []os.Signal, action func(int)) {
    52  	handlers.Lock()
    53  	defer handlers.Unlock()
    54  
    55  	remove := func(n int) {
    56  		var zerohandler handler
    57  
    58  		for c, h := range handlers.m {
    59  			if h.want(n) {
    60  				handlers.ref[n]--
    61  				h.clear(n)
    62  				if h.mask == zerohandler.mask {
    63  					delete(handlers.m, c)
    64  				}
    65  			}
    66  		}
    67  
    68  		action(n)
    69  	}
    70  
    71  	if len(sigs) == 0 {
    72  		for n := 0; n < numSig; n++ {
    73  			remove(n)
    74  		}
    75  	} else {
    76  		for _, s := range sigs {
    77  			remove(signum(s))
    78  		}
    79  	}
    80  }
    81  
    82  // Ignore causes the provided signals to be ignored. If they are received by
    83  // the program, nothing will happen. Ignore undoes the effect of any prior
    84  // calls to [Notify] for the provided signals.
    85  // If no signals are provided, all incoming signals will be ignored.
    86  func Ignore(sig ...os.Signal) {
    87  	cancel(sig, ignoreSignal)
    88  }
    89  
    90  // Ignored reports whether sig is currently ignored.
    91  func Ignored(sig os.Signal) bool {
    92  	sn := signum(sig)
    93  	return sn >= 0 && signalIgnored(sn)
    94  }
    95  
    96  var (
    97  	// watchSignalLoopOnce guards calling the conditionally
    98  	// initialized watchSignalLoop. If watchSignalLoop is non-nil,
    99  	// it will be run in a goroutine lazily once Notify is invoked.
   100  	// See Issue 21576.
   101  	watchSignalLoopOnce sync.Once
   102  	watchSignalLoop     func()
   103  )
   104  
   105  // Notify causes package signal to relay incoming signals to c.
   106  // If no signals are provided, all incoming signals will be relayed to c.
   107  // Otherwise, just the provided signals will.
   108  //
   109  // Package signal will not block sending to c: the caller must ensure
   110  // that c has sufficient buffer space to keep up with the expected
   111  // signal rate. For a channel used for notification of just one signal value,
   112  // a buffer of size 1 is sufficient.
   113  //
   114  // It is allowed to call Notify multiple times with the same channel:
   115  // each call expands the set of signals sent to that channel.
   116  // The only way to remove signals from the set is to call [Stop].
   117  //
   118  // It is allowed to call Notify multiple times with different channels
   119  // and the same signals: each channel receives copies of incoming
   120  // signals independently.
   121  func Notify(c chan<- os.Signal, sig ...os.Signal) {
   122  	if c == nil {
   123  		panic("os/signal: Notify using nil channel")
   124  	}
   125  
   126  	handlers.Lock()
   127  	defer handlers.Unlock()
   128  
   129  	h := handlers.m[c]
   130  	if h == nil {
   131  		if handlers.m == nil {
   132  			handlers.m = make(map[chan<- os.Signal]*handler)
   133  		}
   134  		h = new(handler)
   135  		handlers.m[c] = h
   136  	}
   137  
   138  	add := func(n int) {
   139  		if n < 0 {
   140  			return
   141  		}
   142  		if !h.want(n) {
   143  			h.set(n)
   144  			if handlers.ref[n] == 0 {
   145  				enableSignal(n)
   146  
   147  				// The runtime requires that we enable a
   148  				// signal before starting the watcher.
   149  				watchSignalLoopOnce.Do(func() {
   150  					if watchSignalLoop != nil {
   151  						go watchSignalLoop()
   152  					}
   153  				})
   154  			}
   155  			handlers.ref[n]++
   156  		}
   157  	}
   158  
   159  	if len(sig) == 0 {
   160  		for n := 0; n < numSig; n++ {
   161  			add(n)
   162  		}
   163  	} else {
   164  		for _, s := range sig {
   165  			add(signum(s))
   166  		}
   167  	}
   168  }
   169  
   170  // Reset undoes the effect of any prior calls to [Notify] for the provided
   171  // signals.
   172  // If no signals are provided, all signal handlers will be reset.
   173  func Reset(sig ...os.Signal) {
   174  	cancel(sig, disableSignal)
   175  }
   176  
   177  // Stop causes package signal to stop relaying incoming signals to c.
   178  // It undoes the effect of all prior calls to [Notify] using c.
   179  // When Stop returns, it is guaranteed that c will receive no more signals.
   180  func Stop(c chan<- os.Signal) {
   181  	handlers.Lock()
   182  
   183  	h := handlers.m[c]
   184  	if h == nil {
   185  		handlers.Unlock()
   186  		return
   187  	}
   188  	delete(handlers.m, c)
   189  
   190  	for n := 0; n < numSig; n++ {
   191  		if h.want(n) {
   192  			handlers.ref[n]--
   193  			if handlers.ref[n] == 0 {
   194  				disableSignal(n)
   195  			}
   196  		}
   197  	}
   198  
   199  	// Signals will no longer be delivered to the channel.
   200  	// We want to avoid a race for a signal such as SIGINT:
   201  	// it should be either delivered to the channel,
   202  	// or the program should take the default action (that is, exit).
   203  	// To avoid the possibility that the signal is delivered,
   204  	// and the signal handler invoked, and then Stop deregisters
   205  	// the channel before the process function below has a chance
   206  	// to send it on the channel, put the channel on a list of
   207  	// channels being stopped and wait for signal delivery to
   208  	// quiesce before fully removing it.
   209  
   210  	handlers.stopping = append(handlers.stopping, stopping{c, h})
   211  
   212  	handlers.Unlock()
   213  
   214  	signalWaitUntilIdle()
   215  
   216  	handlers.Lock()
   217  
   218  	for i, s := range handlers.stopping {
   219  		if s.c == c {
   220  			handlers.stopping = append(handlers.stopping[:i], handlers.stopping[i+1:]...)
   221  			break
   222  		}
   223  	}
   224  
   225  	handlers.Unlock()
   226  }
   227  
   228  // Wait until there are no more signals waiting to be delivered.
   229  // Defined by the runtime package.
   230  func signalWaitUntilIdle()
   231  
   232  func process(sig os.Signal) {
   233  	n := signum(sig)
   234  	if n < 0 {
   235  		return
   236  	}
   237  
   238  	handlers.Lock()
   239  	defer handlers.Unlock()
   240  
   241  	for c, h := range handlers.m {
   242  		if h.want(n) {
   243  			// send but do not block for it
   244  			select {
   245  			case c <- sig:
   246  			default:
   247  			}
   248  		}
   249  	}
   250  
   251  	// Avoid the race mentioned in Stop.
   252  	for _, d := range handlers.stopping {
   253  		if d.h.want(n) {
   254  			select {
   255  			case d.c <- sig:
   256  			default:
   257  			}
   258  		}
   259  	}
   260  }
   261  
   262  // NotifyContext returns a copy of the parent context that is marked done
   263  // (its Done channel is closed) when one of the listed signals arrives,
   264  // when the returned stop function is called, or when the parent context's
   265  // Done channel is closed, whichever happens first.
   266  //
   267  // The stop function unregisters the signal behavior, which, like [signal.Reset],
   268  // may restore the default behavior for a given signal. For example, the default
   269  // behavior of a Go program receiving [os.Interrupt] is to exit. Calling
   270  // NotifyContext(parent, os.Interrupt) will change the behavior to cancel
   271  // the returned context. Future interrupts received will not trigger the default
   272  // (exit) behavior until the returned stop function is called.
   273  //
   274  // The stop function releases resources associated with it, so code should
   275  // call stop as soon as the operations running in this Context complete and
   276  // signals no longer need to be diverted to the context.
   277  func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
   278  	ctx, cancel := context.WithCancel(parent)
   279  	c := &signalCtx{
   280  		Context: ctx,
   281  		cancel:  cancel,
   282  		signals: signals,
   283  	}
   284  	c.ch = make(chan os.Signal, 1)
   285  	Notify(c.ch, c.signals...)
   286  	if ctx.Err() == nil {
   287  		go func() {
   288  			select {
   289  			case <-c.ch:
   290  				c.cancel()
   291  			case <-c.Done():
   292  			}
   293  		}()
   294  	}
   295  	return c, c.stop
   296  }
   297  
   298  type signalCtx struct {
   299  	context.Context
   300  
   301  	cancel  context.CancelFunc
   302  	signals []os.Signal
   303  	ch      chan os.Signal
   304  }
   305  
   306  func (c *signalCtx) stop() {
   307  	c.cancel()
   308  	Stop(c.ch)
   309  }
   310  
   311  type stringer interface {
   312  	String() string
   313  }
   314  
   315  func (c *signalCtx) String() string {
   316  	var buf []byte
   317  	// We know that the type of c.Context is context.cancelCtx, and we know that the
   318  	// String method of cancelCtx returns a string that ends with ".WithCancel".
   319  	name := c.Context.(stringer).String()
   320  	name = name[:len(name)-len(".WithCancel")]
   321  	buf = append(buf, "signal.NotifyContext("+name...)
   322  	if len(c.signals) != 0 {
   323  		buf = append(buf, ", ["...)
   324  		for i, s := range c.signals {
   325  			buf = append(buf, s.String()...)
   326  			if i != len(c.signals)-1 {
   327  				buf = append(buf, ' ')
   328  			}
   329  		}
   330  		buf = append(buf, ']')
   331  	}
   332  	buf = append(buf, ')')
   333  	return string(buf)
   334  }
   335  

View as plain text