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 signalimport ()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.Signalh *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 handlerfor , := 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.OncewatchSignalLoop 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 = slices.Delete(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 itselect {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.Contextcancel context.CancelFuncsignals []os.Signalch 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.9-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. |