Source file
src/os/signal/signal.go
1
2
3
4
5 package signal
6
7 import (
8 "context"
9 "os"
10 "slices"
11 "sync"
12 )
13
14 var handlers struct {
15 sync.Mutex
16
17 m map[chan<- os.Signal]*handler
18
19 ref [numSig]int64
20
21
22
23
24
25 stopping []stopping
26 }
27
28 type stopping struct {
29 c chan<- os.Signal
30 h *handler
31 }
32
33 type handler struct {
34 mask [(numSig + 31) / 32]uint32
35 }
36
37 func (h *handler) want(sig int) bool {
38 return (h.mask[sig/32]>>uint(sig&31))&1 != 0
39 }
40
41 func (h *handler) set(sig int) {
42 h.mask[sig/32] |= 1 << uint(sig&31)
43 }
44
45 func (h *handler) clear(sig int) {
46 h.mask[sig/32] &^= 1 << uint(sig&31)
47 }
48
49
50
51
52 func cancel(sigs []os.Signal, action func(int)) {
53 handlers.Lock()
54 defer handlers.Unlock()
55
56 remove := func(n int) {
57 if n < 0 {
58 return
59 }
60
61 var zerohandler handler
62
63 for c, h := range handlers.m {
64 if h.want(n) {
65 handlers.ref[n]--
66 h.clear(n)
67 if h.mask == zerohandler.mask {
68 delete(handlers.m, c)
69 }
70 }
71 }
72
73 action(n)
74 }
75
76 if len(sigs) == 0 {
77 for n := 0; n < numSig; n++ {
78 remove(n)
79 }
80 } else {
81 for _, s := range sigs {
82 remove(signum(s))
83 }
84 }
85 }
86
87
88
89
90
91 func Ignore(sig ...os.Signal) {
92 cancel(sig, ignoreSignal)
93 }
94
95
96 func Ignored(sig os.Signal) bool {
97 sn := signum(sig)
98 return sn >= 0 && signalIgnored(sn)
99 }
100
101 var (
102
103
104
105
106 watchSignalLoopOnce sync.Once
107 watchSignalLoop func()
108 )
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 func Notify(c chan<- os.Signal, sig ...os.Signal) {
127 if c == nil {
128 panic("os/signal: Notify using nil channel")
129 }
130
131 handlers.Lock()
132 defer handlers.Unlock()
133
134
135
136 getHandler := func() *handler {
137 h := handlers.m[c]
138 if h == nil {
139 if handlers.m == nil {
140 handlers.m = make(map[chan<- os.Signal]*handler)
141 }
142 h = new(handler)
143 handlers.m[c] = h
144 }
145
146 return h
147 }
148
149 add := func(n int) {
150 if n < 0 {
151 return
152 }
153
154 h := getHandler()
155 if !h.want(n) {
156 h.set(n)
157 if handlers.ref[n] == 0 {
158 enableSignal(n)
159
160
161
162 watchSignalLoopOnce.Do(func() {
163 if watchSignalLoop != nil {
164 go watchSignalLoop()
165 }
166 })
167 }
168 handlers.ref[n]++
169 }
170 }
171
172 if len(sig) == 0 {
173 for n := 0; n < numSig; n++ {
174 add(n)
175 }
176 } else {
177 for _, s := range sig {
178 add(signum(s))
179 }
180 }
181 }
182
183
184
185
186 func Reset(sig ...os.Signal) {
187 cancel(sig, disableSignal)
188 }
189
190
191
192
193 func Stop(c chan<- os.Signal) {
194 handlers.Lock()
195
196 h := handlers.m[c]
197 if h == nil {
198 handlers.Unlock()
199 return
200 }
201 delete(handlers.m, c)
202
203 for n := 0; n < numSig; n++ {
204 if h.want(n) {
205 handlers.ref[n]--
206 if handlers.ref[n] == 0 {
207 disableSignal(n)
208 }
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223 handlers.stopping = append(handlers.stopping, stopping{c, h})
224
225 handlers.Unlock()
226
227 signalWaitUntilIdle()
228
229 handlers.Lock()
230
231 for i, s := range handlers.stopping {
232 if s.c == c {
233 handlers.stopping = slices.Delete(handlers.stopping, i, i+1)
234 break
235 }
236 }
237
238 handlers.Unlock()
239 }
240
241
242
243 func signalWaitUntilIdle()
244
245 func process(sig os.Signal) {
246 n := signum(sig)
247 if n < 0 {
248 return
249 }
250
251 handlers.Lock()
252 defer handlers.Unlock()
253
254 for c, h := range handlers.m {
255 if h.want(n) {
256
257 select {
258 case c <- sig:
259 default:
260 }
261 }
262 }
263
264
265 for _, d := range handlers.stopping {
266 if d.h.want(n) {
267 select {
268 case d.c <- sig:
269 default:
270 }
271 }
272 }
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
294 ctx, cancel := context.WithCancelCause(parent)
295 c := &signalCtx{
296 Context: ctx,
297 cancel: cancel,
298 signals: signals,
299 }
300 c.ch = make(chan os.Signal, 1)
301 Notify(c.ch, c.signals...)
302 if ctx.Err() == nil {
303 go func() {
304 select {
305 case s := <-c.ch:
306 c.cancel(signalError(s.String() + " signal received"))
307 case <-c.Done():
308 }
309 }()
310 }
311 return c, c.stop
312 }
313
314 type signalCtx struct {
315 context.Context
316
317 cancel context.CancelCauseFunc
318 signals []os.Signal
319 ch chan os.Signal
320 }
321
322 func (c *signalCtx) stop() {
323 c.cancel(nil)
324 Stop(c.ch)
325 }
326
327 type stringer interface {
328 String() string
329 }
330
331 func (c *signalCtx) String() string {
332 var buf []byte
333
334
335 name := c.Context.(stringer).String()
336 name = name[:len(name)-len(".WithCancel")]
337 buf = append(buf, "signal.NotifyContext("+name...)
338 if len(c.signals) != 0 {
339 buf = append(buf, ", ["...)
340 for i, s := range c.signals {
341 buf = append(buf, s.String()...)
342 if i != len(c.signals)-1 {
343 buf = append(buf, ' ')
344 }
345 }
346 buf = append(buf, ']')
347 }
348 buf = append(buf, ')')
349 return string(buf)
350 }
351
352 type signalError string
353
354 func (s signalError) Error() string {
355 return string(s)
356 }
357
View as plain text