github.com/AndrienkoAleksandr/go@v0.0.19/src/os/signal/signal.go (about) 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 }