diff options
Diffstat (limited to 'libgo/go/runtime/sigqueue.go')
-rw-r--r-- | libgo/go/runtime/sigqueue.go | 34 |
1 files changed, 32 insertions, 2 deletions
diff --git a/libgo/go/runtime/sigqueue.go b/libgo/go/runtime/sigqueue.go index 7f9badd..ca41b05 100644 --- a/libgo/go/runtime/sigqueue.go +++ b/libgo/go/runtime/sigqueue.go @@ -12,12 +12,16 @@ // 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. +// variable. It can be in 4 states: sigIdle, sigReceiving, sigSending and sigFixup. // 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. +// sigFixup is a transient state that can only exist as a short +// transition from sigReceiving and then on to sigIdle: it is +// used to ensure the AllThreadsSyscall()'s mDoFixup() operation +// occurs on the sleeping m, waiting to receive a signal. // 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 @@ -59,6 +63,7 @@ const ( sigIdle = iota sigReceiving sigSending + sigFixup ) // sigsend delivers a signal from sighandler to the internal signal delivery queue. @@ -112,6 +117,9 @@ Send: notewakeup(&sig.note) break Send } + case sigFixup: + // nothing to do - we need to wait for sigIdle. + osyield() } } @@ -119,6 +127,19 @@ Send: return true } +// sigRecvPrepareForFixup is used to temporarily wake up the +// signal_recv() running thread while it is blocked waiting for the +// arrival of a signal. If it causes the thread to wake up, the +// sig.state travels through this sequence: sigReceiving -> sigFixup +// -> sigIdle -> sigReceiving and resumes. (This is only called while +// GC is disabled.) +//go:nosplit +func sigRecvPrepareForFixup() { + if atomic.Cas(&sig.state, sigReceiving, sigFixup) { + notewakeup(&sig.note) + } +} + // Called to receive the next queued signal. // Must only be called from a single goroutine at a time. //go:linkname signal_recv os_1signal.signal__recv @@ -146,7 +167,16 @@ func signal_recv() uint32 { } notetsleepg(&sig.note, -1) noteclear(&sig.note) - break Receive + if !atomic.Cas(&sig.state, sigFixup, sigIdle) { + break Receive + } + // Getting here, the code will + // loop around again to sleep + // in state sigReceiving. This + // path is taken when + // sigRecvPrepareForFixup() + // has been called by another + // thread. } case sigSending: if atomic.Cas(&sig.state, sigSending, sigIdle) { |