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
5package signal
6
7import (
8	"os"
9	"sync"
10)
11
12var handlers struct {
13	sync.Mutex
14	// Map a channel to the signals that should be sent to it.
15	m map[chan<- os.Signal]*handler
16	// Map a signal to the number of channels receiving it.
17	ref [numSig]int64
18	// Map channels to signals while the channel is being stopped.
19	// Not a map because entries live here only very briefly.
20	// We need a separate container because we need m to correspond to ref
21	// at all times, and we also need to keep track of the *handler
22	// value for a channel being stopped. See the Stop function.
23	stopping []stopping
24}
25
26type stopping struct {
27	c chan<- os.Signal
28	h *handler
29}
30
31type handler struct {
32	mask [(numSig + 31) / 32]uint32
33}
34
35func (h *handler) want(sig int) bool {
36	return (h.mask[sig/32]>>uint(sig&31))&1 != 0
37}
38
39func (h *handler) set(sig int) {
40	h.mask[sig/32] |= 1 << uint(sig&31)
41}
42
43func (h *handler) clear(sig int) {
44	h.mask[sig/32] &^= 1 << uint(sig&31)
45}
46
47// Stop relaying the signals, sigs, to any channels previously registered to
48// receive them and either reset the signal handlers to their original values
49// (action=disableSignal) or ignore the signals (action=ignoreSignal).
50func cancel(sigs []os.Signal, action func(int)) {
51	handlers.Lock()
52	defer handlers.Unlock()
53
54	remove := func(n int) {
55		var zerohandler handler
56
57		for c, h := range handlers.m {
58			if h.want(n) {
59				handlers.ref[n]--
60				h.clear(n)
61				if h.mask == zerohandler.mask {
62					delete(handlers.m, c)
63				}
64			}
65		}
66
67		action(n)
68	}
69
70	if len(sigs) == 0 {
71		for n := 0; n < numSig; n++ {
72			remove(n)
73		}
74	} else {
75		for _, s := range sigs {
76			remove(signum(s))
77		}
78	}
79}
80
81// Ignore causes the provided signals to be ignored. If they are received by
82// the program, nothing will happen. Ignore undoes the effect of any prior
83// calls to Notify for the provided signals.
84// If no signals are provided, all incoming signals will be ignored.
85func Ignore(sig ...os.Signal) {
86	cancel(sig, ignoreSignal)
87}
88
89// Notify causes package signal to relay incoming signals to c.
90// If no signals are provided, all incoming signals will be relayed to c.
91// Otherwise, just the provided signals will.
92//
93// Package signal will not block sending to c: the caller must ensure
94// that c has sufficient buffer space to keep up with the expected
95// signal rate. For a channel used for notification of just one signal value,
96// a buffer of size 1 is sufficient.
97//
98// It is allowed to call Notify multiple times with the same channel:
99// each call expands the set of signals sent to that channel.
100// The only way to remove signals from the set is to call Stop.
101//
102// It is allowed to call Notify multiple times with different channels
103// and the same signals: each channel receives copies of incoming
104// signals independently.
105func Notify(c chan<- os.Signal, sig ...os.Signal) {
106	if c == nil {
107		panic("os/signal: Notify using nil channel")
108	}
109
110	handlers.Lock()
111	defer handlers.Unlock()
112
113	h := handlers.m[c]
114	if h == nil {
115		if handlers.m == nil {
116			handlers.m = make(map[chan<- os.Signal]*handler)
117		}
118		h = new(handler)
119		handlers.m[c] = h
120	}
121
122	add := func(n int) {
123		if n < 0 {
124			return
125		}
126		if !h.want(n) {
127			h.set(n)
128			if handlers.ref[n] == 0 {
129				enableSignal(n)
130			}
131			handlers.ref[n]++
132		}
133	}
134
135	if len(sig) == 0 {
136		for n := 0; n < numSig; n++ {
137			add(n)
138		}
139	} else {
140		for _, s := range sig {
141			add(signum(s))
142		}
143	}
144}
145
146// Reset undoes the effect of any prior calls to Notify for the provided
147// signals.
148// If no signals are provided, all signal handlers will be reset.
149func Reset(sig ...os.Signal) {
150	cancel(sig, disableSignal)
151}
152
153// Stop causes package signal to stop relaying incoming signals to c.
154// It undoes the effect of all prior calls to Notify using c.
155// When Stop returns, it is guaranteed that c will receive no more signals.
156func Stop(c chan<- os.Signal) {
157	handlers.Lock()
158
159	h := handlers.m[c]
160	if h == nil {
161		handlers.Unlock()
162		return
163	}
164	delete(handlers.m, c)
165
166	for n := 0; n < numSig; n++ {
167		if h.want(n) {
168			handlers.ref[n]--
169			if handlers.ref[n] == 0 {
170				disableSignal(n)
171			}
172		}
173	}
174
175	// Signals will no longer be delivered to the channel.
176	// We want to avoid a race for a signal such as SIGINT:
177	// it should be either delivered to the channel,
178	// or the program should take the default action (that is, exit).
179	// To avoid the possibility that the signal is delivered,
180	// and the signal handler invoked, and then Stop deregisters
181	// the channel before the process function below has a chance
182	// to send it on the channel, put the channel on a list of
183	// channels being stopped and wait for signal delivery to
184	// quiesce before fully removing it.
185
186	handlers.stopping = append(handlers.stopping, stopping{c, h})
187
188	handlers.Unlock()
189
190	signalWaitUntilIdle()
191
192	handlers.Lock()
193
194	for i, s := range handlers.stopping {
195		if s.c == c {
196			handlers.stopping = append(handlers.stopping[:i], handlers.stopping[i+1:]...)
197			break
198		}
199	}
200
201	handlers.Unlock()
202}
203
204// Wait until there are no more signals waiting to be delivered.
205// Defined by the runtime package.
206func signalWaitUntilIdle()
207
208func process(sig os.Signal) {
209	n := signum(sig)
210	if n < 0 {
211		return
212	}
213
214	handlers.Lock()
215	defer handlers.Unlock()
216
217	for c, h := range handlers.m {
218		if h.want(n) {
219			// send but do not block for it
220			select {
221			case c <- sig:
222			default:
223			}
224		}
225	}
226
227	// Avoid the race mentioned in Stop.
228	for _, d := range handlers.stopping {
229		if d.h.want(n) {
230			select {
231			case d.c <- sig:
232			default:
233			}
234		}
235	}
236}
237