// 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 3 states: sigIdle, sigReceiving and sigSending. // 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. package runtime import "unsafe" var sig struct { note note mask [(_NSIG + 31) / 32]uint32 wanted [(_NSIG + 31) / 32]uint32 recv [(_NSIG + 31) / 32]uint32 state uint32 inuse bool } const ( sigIdle = iota sigReceiving sigSending ) // Called from sighandler to send a signal back out of the signal handling thread. // Reports whether the signal was sent. If not, the caller typically crashes the program. func sigsend(s int32) bool { bit := uint32(1) << uint(s&31) if !sig.inuse || s < 0 || int(s) >= 32*len(sig.wanted) || sig.wanted[s/32]&bit == 0 { return false } // Add signal to outgoing queue. for { mask := sig.mask[s/32] if mask&bit != 0 { return true // signal already in queue } if cas(&sig.mask[s/32], mask, mask|bit) { break } } // Notify receiver that queue has new bit. Send: for { switch atomicload(&sig.state) { default: gothrow("sigsend: inconsistent state") case sigIdle: if cas(&sig.state, sigIdle, sigSending) { break Send } case sigSending: // notification already pending break Send case sigReceiving: if cas(&sig.state, sigReceiving, sigIdle) { notewakeup(&sig.note) break Send } } } return true } // Called to receive the next queued signal. // Must only be called from a single goroutine at a time. func signal_recv() uint32 { for { // Serve any signals from local copy. for i := uint32(0); i < _NSIG; i++ { if sig.recv[i/32]&(1<<(i&31)) != 0 { sig.recv[i/32] &^= 1 << (i & 31) return i } } // Wait for updates to be available from signal sender. Receive: for { switch atomicload(&sig.state) { default: gothrow("signal_recv: inconsistent state") case sigIdle: if cas(&sig.state, sigIdle, sigReceiving) { notetsleepg(&sig.note, -1) noteclear(&sig.note) break Receive } case sigSending: if cas(&sig.state, sigSending, sigIdle) { break Receive } } } // Incorporate updates from sender into local copy. for i := range sig.mask { sig.recv[i] = xchg(&sig.mask[i], 0) } } } // Must only be called from a single goroutine at a time. func signal_enable(s uint32) { if !sig.inuse { // The first call to signal_enable is for us // to use for initialization. It does not pass // signal information in m. sig.inuse = true // enable reception of signals; cannot disable noteclear(&sig.note) return } if int(s) >= len(sig.wanted)*32 { return } sig.wanted[s/32] |= 1 << (s & 31) sigenable_go(s) } // Must only be called from a single goroutine at a time. func signal_disable(s uint32) { if int(s) >= len(sig.wanted)*32 { return } sig.wanted[s/32] &^= 1 << (s & 31) sigdisable_go(s) } // This runs on a foreign stack, without an m or a g. No stack split. //go:nosplit func badsignal(sig uintptr) { // Some external libraries, for example, OpenBLAS, create worker threads in // a global constructor. If we're doing cpu profiling, and the SIGPROF signal // comes to one of the foreign threads before we make our first cgo call, the // call to cgocallback below will bring down the whole process. // It's better to miss a few SIGPROF signals than to abort in this case. // See http://golang.org/issue/9456. if _SIGPROF != 0 && sig == _SIGPROF && needextram != 0 { return } cgocallback(unsafe.Pointer(funcPC(sigsend)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig)) } func sigenable_m() func sigdisable_m() func sigenable_go(s uint32) { g := getg() g.m.scalararg[0] = uintptr(s) onM(sigenable_m) } func sigdisable_go(s uint32) { g := getg() g.m.scalararg[0] = uintptr(s) onM(sigdisable_m) }