Source File
signal.go
Belonging Package
os/signal
// 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 (
)
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 ( *handler) ( int) bool {
return (.mask[/32]>>uint(&31))&1 != 0
}
func ( *handler) ( int) {
.mask[/32] |= 1 << uint(&31)
}
func ( *handler) ( int) {
.mask[/32] &^= 1 << uint(&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( []os.Signal, func(int)) {
handlers.Lock()
defer handlers.Unlock()
:= func( int) {
var handler
for , := range handlers.m {
if .want() {
handlers.ref[]--
.clear()
if .mask == .mask {
delete(handlers.m, )
}
}
}
()
}
if len() == 0 {
for := 0; < numSig; ++ {
()
}
} else {
for , := range {
(signum())
}
}
}
// 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 ( ...os.Signal) {
cancel(, ignoreSignal)
}
// Ignored reports whether sig is currently ignored.
func ( os.Signal) bool {
:= signum()
return >= 0 && signalIgnored()
}
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 ( chan<- os.Signal, ...os.Signal) {
if == nil {
panic("os/signal: Notify using nil channel")
}
handlers.Lock()
defer handlers.Unlock()
:= handlers.m[]
if == nil {
if handlers.m == nil {
handlers.m = make(map[chan<- os.Signal]*handler)
}
= new(handler)
handlers.m[] =
}
:= func( int) {
if < 0 {
return
}
if !.want() {
.set()
if handlers.ref[] == 0 {
enableSignal()
// The runtime requires that we enable a
// signal before starting the watcher.
watchSignalLoopOnce.Do(func() {
if watchSignalLoop != nil {
go watchSignalLoop()
}
})
}
handlers.ref[]++
}
}
if len() == 0 {
for := 0; < numSig; ++ {
()
}
} else {
for , := range {
(signum())
}
}
}
// 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 ( ...os.Signal) {
cancel(, 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 ( chan<- os.Signal) {
handlers.Lock()
:= handlers.m[]
if == nil {
handlers.Unlock()
return
}
delete(handlers.m, )
for := 0; < numSig; ++ {
if .want() {
handlers.ref[]--
if handlers.ref[] == 0 {
disableSignal()
}
}
}
// 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{, })
handlers.Unlock()
signalWaitUntilIdle()
handlers.Lock()
for , := range handlers.stopping {
if .c == {
handlers.stopping = append(handlers.stopping[:], handlers.stopping[+1:]...)
break
}
}
handlers.Unlock()
}
// Wait until there are no more signals waiting to be delivered.
// Defined by the runtime package.
func signalWaitUntilIdle()
func process( os.Signal) {
:= signum()
if < 0 {
return
}
handlers.Lock()
defer handlers.Unlock()
for , := range handlers.m {
if .want() {
// send but do not block for it
select {
case <- :
default:
}
}
}
// Avoid the race mentioned in Stop.
for , := range handlers.stopping {
if .h.want() {
select {
case .c <- :
default:
}
}
}
}
// NotifyContext returns a copy of the parent context that is marked done
// (its Done channel is closed) when one of the listed signals arrives,
// when the returned stop function is called, or when the parent context's
// Done channel is closed, whichever happens first.
//
// The stop function unregisters the signal behavior, which, like [signal.Reset],
// may restore the default behavior for a given signal. For example, the default
// behavior of a Go program receiving [os.Interrupt] is to exit. Calling
// NotifyContext(parent, os.Interrupt) will change the behavior to cancel
// the returned context. Future interrupts received will not trigger the default
// (exit) behavior until the returned stop function is called.
//
// The stop function releases resources associated with it, so code should
// call stop as soon as the operations running in this Context complete and
// signals no longer need to be diverted to the context.
func ( context.Context, ...os.Signal) ( context.Context, context.CancelFunc) {
, := context.WithCancel()
:= &signalCtx{
Context: ,
cancel: ,
signals: ,
}
.ch = make(chan os.Signal, 1)
Notify(.ch, .signals...)
if .Err() == nil {
go func() {
select {
case <-.ch:
.cancel()
case <-.Done():
}
}()
}
return , .stop
}
type signalCtx struct {
context.Context
cancel context.CancelFunc
signals []os.Signal
ch chan os.Signal
}
func ( *signalCtx) () {
.cancel()
Stop(.ch)
}
type stringer interface {
String() string
}
func ( *signalCtx) () string {
var []byte
// We know that the type of c.Context is context.cancelCtx, and we know that the
// String method of cancelCtx returns a string that ends with ".WithCancel".
:= .Context.(stringer).String()
= [:len()-len(".WithCancel")]
= append(, "signal.NotifyContext("+...)
if len(.signals) != 0 {
= append(, ", ["...)
for , := range .signals {
= append(, .String()...)
if != len(.signals)-1 {
= append(, ' ')
}
}
= append(, ']')
}
= append(, ')')
return string()
}
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |