Commit f93e21ac authored by Russ Cox's avatar Russ Cox

runtime: clean up sigqueue.go

Minor changes to make logic clearer.
Observed while working on the conversion.

LGTM=iant, dvyukov
R=dvyukov, iant
CC=golang-codereviews
https://golang.org/cl/140250043
parent 0f9b6aff
...@@ -9,19 +9,19 @@ ...@@ -9,19 +9,19 @@
// so the handler communicates with a processing goroutine // so the handler communicates with a processing goroutine
// via struct sig, below. // via struct sig, below.
// //
// sigsend() is called by the signal handler to queue a new signal. // 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. // 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 // Synchronization between sigsend and signal_recv is based on the sig.state
// variable. It can be in 3 states: 0, HASWAITER and HASSIGNAL. // variable. It can be in 3 states: sigIdle, sigReceiving and sigSending.
// HASWAITER means that signal_recv() is blocked on sig.Note and there are no // sigReceiving means that signal_recv is blocked on sig.Note and there are no
// new pending signals. // new pending signals.
// HASSIGNAL means that sig.mask *may* contain new pending signals, // sigSending means that sig.mask *may* contain new pending signals,
// signal_recv() can't be blocked in this state. // signal_recv can't be blocked in this state.
// 0 means that there are no new pending signals and signal_recv() is not blocked. // sigIdle means that there are no new pending signals and signal_recv is not blocked.
// Transitions between states are done atomically with CAS. // Transitions between states are done atomically with CAS.
// When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask. // When signal_recv is unblocked, it resets sig.Note and rechecks sig.mask.
// If several sigsend()'s and signal_recv() execute concurrently, it can lead to // If several sigsends and signal_recv execute concurrently, it can lead to
// unnecessary rechecks of sig.mask, but must not lead to missed signals // unnecessary rechecks of sig.mask, but it cannot lead to missed signals
// nor deadlocks. // nor deadlocks.
package runtime package runtime
...@@ -38,47 +38,51 @@ var sig struct { ...@@ -38,47 +38,51 @@ var sig struct {
} }
const ( const (
_HASWAITER = 1 sigIdle = iota
_HASSIGNAL = 2 sigReceiving
sigSending
) )
// Called from sighandler to send a signal back out of the signal handling thread. // 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 { func sigsend(s int32) bool {
bit := uint32(1) << uint(s&31) bit := uint32(1) << uint(s&31)
if !sig.inuse || s < 0 || int(s) >= 32*len(sig.wanted) || sig.wanted[s/32]&bit == 0 { if !sig.inuse || s < 0 || int(s) >= 32*len(sig.wanted) || sig.wanted[s/32]&bit == 0 {
return false return false
} }
// Add signal to outgoing queue.
for { for {
mask := sig.mask[s/32] mask := sig.mask[s/32]
if mask&bit != 0 { if mask&bit != 0 {
break // signal already in queue return true // signal already in queue
} }
if cas(&sig.mask[s/32], mask, mask|bit) { if cas(&sig.mask[s/32], mask, mask|bit) {
// Added to queue. break
// Only send a wakeup if the receiver needs a kick. }
for { }
old := atomicload(&sig.state)
if old == _HASSIGNAL {
break
}
var new uint32 // Notify receiver that queue has new bit.
if old == _HASWAITER { Send:
new = 0 for {
} else { // old == 0 switch atomicload(&sig.state) {
new = _HASSIGNAL default:
} gothrow("sigsend: inconsistent state")
if cas(&sig.state, old, new) { case sigIdle:
if old == _HASWAITER { if cas(&sig.state, sigIdle, sigSending) {
notewakeup(&sig.note) break Send
} }
break case sigSending:
} // notification already pending
break Send
case sigReceiving:
if cas(&sig.state, sigReceiving, sigIdle) {
notewakeup(&sig.note)
break Send
} }
break
} }
} }
return true return true
} }
...@@ -86,7 +90,7 @@ func sigsend(s int32) bool { ...@@ -86,7 +90,7 @@ func sigsend(s int32) bool {
// Must only be called from a single goroutine at a time. // Must only be called from a single goroutine at a time.
func signal_recv() uint32 { func signal_recv() uint32 {
for { for {
// Serve from local copy if there are bits left. // Serve any signals from local copy.
for i := uint32(0); i < _NSIG; i++ { for i := uint32(0); i < _NSIG; i++ {
if sig.recv[i/32]&(1<<(i&31)) != 0 { if sig.recv[i/32]&(1<<(i&31)) != 0 {
sig.recv[i/32] &^= 1 << (i & 31) sig.recv[i/32] &^= 1 << (i & 31)
...@@ -94,38 +98,28 @@ func signal_recv() uint32 { ...@@ -94,38 +98,28 @@ func signal_recv() uint32 {
} }
} }
// Check and update sig.state. // Wait for updates to be available from signal sender.
Receive:
for { for {
old := atomicload(&sig.state) switch atomicload(&sig.state) {
if old == _HASWAITER { default:
gothrow("inconsistent state in signal_recv") gothrow("signal_recv: inconsistent state")
} case sigIdle:
if cas(&sig.state, sigIdle, sigReceiving) {
var new uint32
if old == _HASSIGNAL {
new = 0
} else { // old == 0
new = _HASWAITER
}
if cas(&sig.state, old, new) {
if new == _HASWAITER {
notetsleepg(&sig.note, -1) notetsleepg(&sig.note, -1)
noteclear(&sig.note) noteclear(&sig.note)
break Receive
}
case sigSending:
if cas(&sig.state, sigSending, sigIdle) {
break Receive
} }
break
} }
} }
// Get a new local copy. // Incorporate updates from sender into local copy.
for i := range sig.mask { for i := range sig.mask {
var m uint32 sig.recv[i] = xchg(&sig.mask[i], 0)
for {
m = sig.mask[i]
if cas(&sig.mask[i], m, 0) {
break
}
}
sig.recv[i] = m
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment