// 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) } // 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") } 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: } } } }