Commit eb97160f authored by Ian Lance Taylor's avatar Ian Lance Taylor

runtime: don't block signals that will kill the program

Otherwise we may delay the delivery of these signals for an arbitrary
length of time. We are already careful to not block signals that the
program has asked to see.

Also make sure that we don't miss a signal delivery if a thread
decides to stop for a while while executing the signal handler.

Also clean up the TestAtomicStop output a little bit.

Fixes #21433

Change-Id: Ic0c1a4eaf7eba80d1abc1e9537570bf4687c2434
Reviewed-on: https://go-review.googlesource.com/79581
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAustin Clements <austin@google.com>
parent b23096b5
...@@ -321,7 +321,9 @@ func TestAtomicStop(t *testing.T) { ...@@ -321,7 +321,9 @@ func TestAtomicStop(t *testing.T) {
cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1") cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err == nil { if err == nil {
t.Logf("iteration %d: output %s", i, out) if len(out) > 0 {
t.Logf("iteration %d: output %s", i, out)
}
} else { } else {
t.Logf("iteration %d: exit status %q: output: %s", i, err, out) t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
} }
...@@ -378,7 +380,7 @@ func atomicStopTestProgram() { ...@@ -378,7 +380,7 @@ func atomicStopTestProgram() {
case <-cs: case <-cs:
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
if !printed { if !printed {
fmt.Print("lost signal on iterations:") fmt.Print("lost signal on tries:")
printed = true printed = true
} }
fmt.Printf(" %d", i) fmt.Printf(" %d", i)
......
...@@ -622,7 +622,7 @@ const ( ...@@ -622,7 +622,7 @@ const (
_SigDefault // if the signal isn't explicitly requested, don't monitor it _SigDefault // if the signal isn't explicitly requested, don't monitor it
_SigGoExit // cause all runtime procs to exit (only used on Plan 9). _SigGoExit // cause all runtime procs to exit (only used on Plan 9).
_SigSetStack // add SA_ONSTACK to libc handler _SigSetStack // add SA_ONSTACK to libc handler
_SigUnblock // unblocked in minit _SigUnblock // always unblock; see blockableSig
_SigIgn // _SIG_DFL action is to ignore the signal _SigIgn // _SIG_DFL action is to ignore the signal
) )
......
...@@ -526,7 +526,7 @@ func ensureSigM() { ...@@ -526,7 +526,7 @@ func ensureSigM() {
// mask accordingly. // mask accordingly.
sigBlocked := sigset_all sigBlocked := sigset_all
for i := range sigtable { for i := range sigtable {
if sigtable[i].flags&_SigUnblock != 0 { if !blockableSig(uint32(i)) {
sigdelset(&sigBlocked, i) sigdelset(&sigBlocked, i)
} }
} }
...@@ -538,7 +538,7 @@ func ensureSigM() { ...@@ -538,7 +538,7 @@ func ensureSigM() {
sigdelset(&sigBlocked, int(sig)) sigdelset(&sigBlocked, int(sig))
} }
case sig := <-disableSigChan: case sig := <-disableSigChan:
if sig > 0 { if sig > 0 && blockableSig(sig) {
sigaddset(&sigBlocked, int(sig)) sigaddset(&sigBlocked, int(sig))
} }
} }
...@@ -736,7 +736,7 @@ func minitSignalStack() { ...@@ -736,7 +736,7 @@ func minitSignalStack() {
func minitSignalMask() { func minitSignalMask() {
nmask := getg().m.sigmask nmask := getg().m.sigmask
for i := range sigtable { for i := range sigtable {
if sigtable[i].flags&_SigUnblock != 0 { if !blockableSig(uint32(i)) {
sigdelset(&nmask, i) sigdelset(&nmask, i)
} }
} }
...@@ -757,6 +757,25 @@ func unminitSignals() { ...@@ -757,6 +757,25 @@ func unminitSignals() {
} }
} }
// blockableSig returns whether sig may be blocked by the signal mask.
// We never want to block the signals marked _SigUnblock;
// these are the synchronous signals that turn into a Go panic.
// In a Go program--not a c-archive/c-shared--we never want to block
// the signals marked _SigKill or _SigThrow, as otherwise it's possible
// for all running threads to block them and delay their delivery until
// we start a new thread. When linked into a C program we let the C code
// decide on the disposition of those signals.
func blockableSig(sig uint32) bool {
flags := sigtable[sig].flags
if flags&_SigUnblock != 0 {
return false
}
if isarchive || islibrary {
return true
}
return flags&(_SigKill|_SigThrow) == 0
}
// gsignalStack saves the fields of the gsignal stack changed by // gsignalStack saves the fields of the gsignal stack changed by
// setGsignalStack. // setGsignalStack.
type gsignalStack struct { type gsignalStack struct {
......
...@@ -45,13 +45,14 @@ import ( ...@@ -45,13 +45,14 @@ import (
// as there is no connection between handling a signal and receiving one, // as there is no connection between handling a signal and receiving one,
// but atomic instructions should minimize it. // but atomic instructions should minimize it.
var sig struct { var sig struct {
note note note note
mask [(_NSIG + 31) / 32]uint32 mask [(_NSIG + 31) / 32]uint32
wanted [(_NSIG + 31) / 32]uint32 wanted [(_NSIG + 31) / 32]uint32
ignored [(_NSIG + 31) / 32]uint32 ignored [(_NSIG + 31) / 32]uint32
recv [(_NSIG + 31) / 32]uint32 recv [(_NSIG + 31) / 32]uint32
state uint32 state uint32
inuse bool delivering uint32
inuse bool
} }
const ( const (
...@@ -68,7 +69,11 @@ func sigsend(s uint32) bool { ...@@ -68,7 +69,11 @@ func sigsend(s uint32) bool {
return false return false
} }
atomic.Xadd(&sig.delivering, 1)
// We are running in the signal handler; defer is not available.
if w := atomic.Load(&sig.wanted[s/32]); w&bit == 0 { if w := atomic.Load(&sig.wanted[s/32]); w&bit == 0 {
atomic.Xadd(&sig.delivering, -1)
return false return false
} }
...@@ -76,6 +81,7 @@ func sigsend(s uint32) bool { ...@@ -76,6 +81,7 @@ func sigsend(s uint32) bool {
for { for {
mask := sig.mask[s/32] mask := sig.mask[s/32]
if mask&bit != 0 { if mask&bit != 0 {
atomic.Xadd(&sig.delivering, -1)
return true // signal already in queue return true // signal already in queue
} }
if atomic.Cas(&sig.mask[s/32], mask, mask|bit) { if atomic.Cas(&sig.mask[s/32], mask, mask|bit) {
...@@ -104,6 +110,7 @@ Send: ...@@ -104,6 +110,7 @@ Send:
} }
} }
atomic.Xadd(&sig.delivering, -1)
return true return true
} }
...@@ -155,6 +162,15 @@ func signal_recv() uint32 { ...@@ -155,6 +162,15 @@ func signal_recv() uint32 {
// by the os/signal package. // by the os/signal package.
//go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle //go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle
func signalWaitUntilIdle() { func signalWaitUntilIdle() {
// 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 atomic.Load(&sig.delivering) != 0 {
Gosched()
}
// Although WaitUntilIdle seems like the right name for this // Although WaitUntilIdle seems like the right name for this
// function, the state we are looking for is sigReceiving, not // function, the state we are looking for is sigReceiving, not
// sigIdle. The sigIdle state is really more like sigProcessing. // sigIdle. The sigIdle state is really more like sigProcessing.
......
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