aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/runtime/sigqueue.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/sigqueue.go')
-rw-r--r--libgo/go/runtime/sigqueue.go34
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) {