Source File
sigqueue.go
Belonging Package
runtime
// Copyright 2009 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.// This file implements runtime support for signal handling.//// Most synchronization primitives are not available from// the signal handler (it cannot block, allocate memory, or use locks)// so the handler communicates with a processing goroutine// via struct sig, below.//// sigsend is called by the signal handler to queue a new signal.// signal_recv is called by the Go program to receive a newly queued signal.//// Synchronization between sigsend and signal_recv is based on the sig.state// variable. It can be in three states:// * sigReceiving means that signal_recv is blocked on sig.Note and there are// no new pending signals.// * sigSending means that sig.mask *may* contain new pending signals,// signal_recv can't be blocked in this state.// * sigIdle means that there are no new pending signals and signal_recv is not// blocked.//// Transitions between states are done atomically with CAS.//// When signal_recv is unblocked, it resets sig.Note and rechecks sig.mask.// If several sigsends and signal_recv execute concurrently, it can lead to// unnecessary rechecks of sig.mask, but it cannot lead to missed signals// nor deadlocks.//go:build !plan9package runtimeimport (_ // for go:linkname)// sig handles communication between the signal handler and os/signal.// Other than the inuse and recv fields, the fields are accessed atomically.//// The wanted and ignored fields are only written by one goroutine at// a time; access is controlled by the handlers Mutex in os/signal.// The fields are only read by that one goroutine and by the signal handler.// We access them atomically to minimize the race between setting them// in the goroutine calling os/signal and the signal handler,// which may be running in a different thread. That race is unavoidable,// as there is no connection between handling a signal and receiving one,// but atomic instructions should minimize it.var sig struct {note notemask [(_NSIG + 31) / 32]uint32wanted [(_NSIG + 31) / 32]uint32ignored [(_NSIG + 31) / 32]uint32recv [(_NSIG + 31) / 32]uint32state atomic.Uint32delivering atomic.Uint32inuse bool}const (sigIdle = iotasigReceivingsigSending)// sigsend delivers a signal from sighandler to the internal signal delivery queue.// It reports whether the signal was sent. If not, the caller typically crashes the program.// It runs from the signal handler, so it's limited in what it can do.func ( uint32) bool {:= uint32(1) << uint(&31)if >= uint32(32*len(sig.wanted)) {return false}sig.delivering.Add(1)// We are running in the signal handler; defer is not available.if := atomic.Load(&sig.wanted[/32]); & == 0 {sig.delivering.Add(-1)return false}// Add signal to outgoing queue.for {:= sig.mask[/32]if & != 0 {sig.delivering.Add(-1)return true // signal already in queue}if atomic.Cas(&sig.mask[/32], , |) {break}}// Notify receiver that queue has new bit.:for {switch sig.state.Load() {default:throw("sigsend: inconsistent state")case sigIdle:if sig.state.CompareAndSwap(sigIdle, sigSending) {break}case sigSending:// notification already pendingbreakcase sigReceiving:if sig.state.CompareAndSwap(sigReceiving, sigIdle) {if GOOS == "darwin" || GOOS == "ios" {sigNoteWakeup(&sig.note)break}notewakeup(&sig.note)break}}}sig.delivering.Add(-1)return true}// Called to receive the next queued signal.// Must only be called from a single goroutine at a time.////go:linkname signal_recv os/signal.signal_recvfunc () uint32 {for {// Serve any signals from local copy.for := uint32(0); < _NSIG; ++ {if sig.recv[/32]&(1<<(&31)) != 0 {sig.recv[/32] &^= 1 << ( & 31)return}}// Wait for updates to be available from signal sender.:for {switch sig.state.Load() {default:throw("signal_recv: inconsistent state")case sigIdle:if sig.state.CompareAndSwap(sigIdle, sigReceiving) {if GOOS == "darwin" || GOOS == "ios" {sigNoteSleep(&sig.note)break}notetsleepg(&sig.note, -1)noteclear(&sig.note)break}case sigSending:if sig.state.CompareAndSwap(sigSending, sigIdle) {break}}}// Incorporate updates from sender into local copy.for := range sig.mask {sig.recv[] = atomic.Xchg(&sig.mask[], 0)}}}// signalWaitUntilIdle waits until the signal delivery mechanism is idle.// This is used to ensure that we do not drop a signal notification due// to a race between disabling a signal and receiving a signal.// This assumes that signal delivery has already been disabled for// the signal(s) in question, and here we are just waiting to make sure// that all the signals have been delivered to the user channels// by the os/signal package.////go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdlefunc () {// Although the signals we care about have been removed from// sig.wanted, it is possible that another thread has received// a signal, has read from sig.wanted, is now updating sig.mask,// and has not yet woken up the processor thread. We need to wait// until all current signal deliveries have completed.for sig.delivering.Load() != 0 {Gosched()}// Although WaitUntilIdle seems like the right name for this// function, the state we are looking for is sigReceiving, not// sigIdle. The sigIdle state is really more like sigProcessing.for sig.state.Load() != sigReceiving {Gosched()}}// Must only be called from a single goroutine at a time.////go:linkname signal_enable os/signal.signal_enablefunc ( uint32) {if !sig.inuse {// This is the first call to signal_enable. Initialize.sig.inuse = true // enable reception of signals; cannot disableif GOOS == "darwin" || GOOS == "ios" {sigNoteSetup(&sig.note)} else {noteclear(&sig.note)}}if >= uint32(len(sig.wanted)*32) {return}:= sig.wanted[/32]|= 1 << ( & 31)atomic.Store(&sig.wanted[/32], ):= sig.ignored[/32]&^= 1 << ( & 31)atomic.Store(&sig.ignored[/32], )sigenable()}// Must only be called from a single goroutine at a time.////go:linkname signal_disable os/signal.signal_disablefunc ( uint32) {if >= uint32(len(sig.wanted)*32) {return}sigdisable():= sig.wanted[/32]&^= 1 << ( & 31)atomic.Store(&sig.wanted[/32], )}// Must only be called from a single goroutine at a time.////go:linkname signal_ignore os/signal.signal_ignorefunc ( uint32) {if >= uint32(len(sig.wanted)*32) {return}sigignore():= sig.wanted[/32]&^= 1 << ( & 31)atomic.Store(&sig.wanted[/32], ):= sig.ignored[/32]|= 1 << ( & 31)atomic.Store(&sig.ignored[/32], )}// sigInitIgnored marks the signal as already ignored. This is called at// program start by initsig. In a shared library initsig is called by// libpreinit, so the runtime may not be initialized yet.////go:nosplitfunc ( uint32) {:= sig.ignored[/32]|= 1 << ( & 31)atomic.Store(&sig.ignored[/32], )}// Checked by signal handlers.////go:linkname signal_ignored os/signal.signal_ignoredfunc ( uint32) bool {:= atomic.Load(&sig.ignored[/32])return &(1<<(&31)) != 0}
![]() |
The pages are generated with Golds v0.6.7. (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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |