| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package signal |
| |
| import ( |
| "os" |
| "sync" |
| ) |
| |
| var handlers struct { |
| sync.Mutex |
| // Map a channel to the signals that should be sent to it. |
| m map[chan<- os.Signal]*handler |
| // Map a signal to the number of channels receiving it. |
| ref [numSig]int64 |
| // Map channels to signals while the channel is being stopped. |
| // Not a map because entries live here only very briefly. |
| // We need a separate container because we need m to correspond to ref |
| // at all times, and we also need to keep track of the *handler |
| // value for a channel being stopped. See the Stop function. |
| stopping []stopping |
| } |
| |
| type stopping struct { |
| c chan<- os.Signal |
| h *handler |
| } |
| |
| type handler struct { |
| mask [(numSig + 31) / 32]uint32 |
| } |
| |
| func (h *handler) want(sig int) bool { |
| return (h.mask[sig/32]>>uint(sig&31))&1 != 0 |
| } |
| |
| func (h *handler) set(sig int) { |
| h.mask[sig/32] |= 1 << uint(sig&31) |
| } |
| |
| func (h *handler) clear(sig int) { |
| h.mask[sig/32] &^= 1 << uint(sig&31) |
| } |
| |
| // Stop relaying the signals, sigs, to any channels previously registered to |
| // receive them and either reset the signal handlers to their original values |
| // (action=disableSignal) or ignore the signals (action=ignoreSignal). |
| func cancel(sigs []os.Signal, action func(int)) { |
| handlers.Lock() |
| defer handlers.Unlock() |
| |
| remove := func(n int) { |
| var zerohandler handler |
| |
| for c, h := range handlers.m { |
| if h.want(n) { |
| handlers.ref[n]-- |
| h.clear(n) |
| if h.mask == zerohandler.mask { |
| delete(handlers.m, c) |
| } |
| } |
| } |
| |
| action(n) |
| } |
| |
| if len(sigs) == 0 { |
| for n := 0; n < numSig; n++ { |
| remove(n) |
| } |
| } else { |
| for _, s := range sigs { |
| remove(signum(s)) |
| } |
| } |
| } |
| |
| // Ignore causes the provided signals to be ignored. If they are received by |
| // the program, nothing will happen. Ignore undoes the effect of any prior |
| // calls to Notify for the provided signals. |
| // If no signals are provided, all incoming signals will be ignored. |
| func Ignore(sig ...os.Signal) { |
| cancel(sig, ignoreSignal) |
| } |
| |
| // Ignored reports whether sig is currently ignored. |
| func Ignored(sig os.Signal) bool { |
| sn := signum(sig) |
| return sn >= 0 && signalIgnored(sn) |
| } |
| |
| var ( |
| // watchSignalLoopOnce guards calling the conditionally |
| // initialized watchSignalLoop. If watchSignalLoop is non-nil, |
| // it will be run in a goroutine lazily once Notify is invoked. |
| // See Issue 21576. |
| watchSignalLoopOnce sync.Once |
| watchSignalLoop func() |
| ) |
| |
| // Notify causes package signal to relay incoming signals to c. |
| // If no signals are provided, all incoming signals will be relayed to c. |
| // Otherwise, just the provided signals will. |
| // |
| // Package signal will not block sending to c: the caller must ensure |
| // that c has sufficient buffer space to keep up with the expected |
| // signal rate. For a channel used for notification of just one signal value, |
| // a buffer of size 1 is sufficient. |
| // |
| // It is allowed to call Notify multiple times with the same channel: |
| // each call expands the set of signals sent to that channel. |
| // The only way to remove signals from the set is to call Stop. |
| // |
| // It is allowed to call Notify multiple times with different channels |
| // and the same signals: each channel receives copies of incoming |
| // signals independently. |
| func Notify(c chan<- os.Signal, sig ...os.Signal) { |
| if c == nil { |
| panic("os/signal: Notify using nil channel") |
| } |
| |
| watchSignalLoopOnce.Do(func() { |
| if watchSignalLoop != nil { |
| go watchSignalLoop() |
| } |
| }) |
| |
| handlers.Lock() |
| defer handlers.Unlock() |
| |
| h := handlers.m[c] |
| if h == nil { |
| if handlers.m == nil { |
| handlers.m = make(map[chan<- os.Signal]*handler) |
| } |
| h = new(handler) |
| handlers.m[c] = h |
| } |
| |
| add := func(n int) { |
| if n < 0 { |
| return |
| } |
| if !h.want(n) { |
| h.set(n) |
| if handlers.ref[n] == 0 { |
| enableSignal(n) |
| } |
| handlers.ref[n]++ |
| } |
| } |
| |
| if len(sig) == 0 { |
| for n := 0; n < numSig; n++ { |
| add(n) |
| } |
| } else { |
| for _, s := range sig { |
| add(signum(s)) |
| } |
| } |
| } |
| |
| // Reset undoes the effect of any prior calls to Notify for the provided |
| // signals. |
| // If no signals are provided, all signal handlers will be reset. |
| func Reset(sig ...os.Signal) { |
| cancel(sig, disableSignal) |
| } |
| |
| // Stop causes package signal to stop relaying incoming signals to c. |
| // It undoes the effect of all prior calls to Notify using c. |
| // When Stop returns, it is guaranteed that c will receive no more signals. |
| func Stop(c chan<- os.Signal) { |
| handlers.Lock() |
| |
| h := handlers.m[c] |
| if h == nil { |
| handlers.Unlock() |
| return |
| } |
| delete(handlers.m, c) |
| |
| for n := 0; n < numSig; n++ { |
| if h.want(n) { |
| handlers.ref[n]-- |
| if handlers.ref[n] == 0 { |
| disableSignal(n) |
| } |
| } |
| } |
| |
| // Signals will no longer be delivered to the channel. |
| // We want to avoid a race for a signal such as SIGINT: |
| // it should be either delivered to the channel, |
| // or the program should take the default action (that is, exit). |
| // To avoid the possibility that the signal is delivered, |
| // and the signal handler invoked, and then Stop deregisters |
| // the channel before the process function below has a chance |
| // to send it on the channel, put the channel on a list of |
| // channels being stopped and wait for signal delivery to |
| // quiesce before fully removing it. |
| |
| handlers.stopping = append(handlers.stopping, stopping{c, h}) |
| |
| handlers.Unlock() |
| |
| signalWaitUntilIdle() |
| |
| handlers.Lock() |
| |
| for i, s := range handlers.stopping { |
| if s.c == c { |
| handlers.stopping = append(handlers.stopping[:i], handlers.stopping[i+1:]...) |
| break |
| } |
| } |
| |
| handlers.Unlock() |
| } |
| |
| // Wait until there are no more signals waiting to be delivered. |
| // Defined by the runtime package. |
| func signalWaitUntilIdle() |
| |
| func process(sig os.Signal) { |
| n := signum(sig) |
| if n < 0 { |
| return |
| } |
| |
| handlers.Lock() |
| defer handlers.Unlock() |
| |
| for c, h := range handlers.m { |
| if h.want(n) { |
| // send but do not block for it |
| select { |
| case c <- sig: |
| default: |
| } |
| } |
| } |
| |
| // Avoid the race mentioned in Stop. |
| for _, d := range handlers.stopping { |
| if d.h.want(n) { |
| select { |
| case d.c <- sig: |
| default: |
| } |
| } |
| } |
| } |