cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
out, err := cmd.CombinedOutput()
if err == nil {
- t.Logf("iteration %d: output %s", i, out)
+ if len(out) > 0 {
+ t.Logf("iteration %d: output %s", i, out)
+ }
} else {
t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
}
case <-cs:
case <-time.After(1 * time.Second):
if !printed {
- fmt.Print("lost signal on iterations:")
+ fmt.Print("lost signal on tries:")
printed = true
}
fmt.Printf(" %d", i)
_SigDefault // if the signal isn't explicitly requested, don't monitor it
_SigGoExit // cause all runtime procs to exit (only used on Plan 9).
_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
)
// mask accordingly.
sigBlocked := sigset_all
for i := range sigtable {
- if sigtable[i].flags&_SigUnblock != 0 {
+ if !blockableSig(uint32(i)) {
sigdelset(&sigBlocked, i)
}
}
sigdelset(&sigBlocked, int(sig))
}
case sig := <-disableSigChan:
- if sig > 0 {
+ if sig > 0 && blockableSig(sig) {
sigaddset(&sigBlocked, int(sig))
}
}
func minitSignalMask() {
nmask := getg().m.sigmask
for i := range sigtable {
- if sigtable[i].flags&_SigUnblock != 0 {
+ if !blockableSig(uint32(i)) {
sigdelset(&nmask, i)
}
}
}
}
+// 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
// setGsignalStack.
type gsignalStack struct {
// as there is no connection between handling a signal and receiving one,
// but atomic instructions should minimize it.
var sig struct {
- note note
- mask [(_NSIG + 31) / 32]uint32
- wanted [(_NSIG + 31) / 32]uint32
- ignored [(_NSIG + 31) / 32]uint32
- recv [(_NSIG + 31) / 32]uint32
- state uint32
- inuse bool
+ note note
+ mask [(_NSIG + 31) / 32]uint32
+ wanted [(_NSIG + 31) / 32]uint32
+ ignored [(_NSIG + 31) / 32]uint32
+ recv [(_NSIG + 31) / 32]uint32
+ state uint32
+ delivering uint32
+ inuse bool
}
const (
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 {
+ atomic.Xadd(&sig.delivering, -1)
return false
}
for {
mask := sig.mask[s/32]
if mask&bit != 0 {
+ atomic.Xadd(&sig.delivering, -1)
return true // signal already in queue
}
if atomic.Cas(&sig.mask[s/32], mask, mask|bit) {
}
}
+ atomic.Xadd(&sig.delivering, -1)
return true
}
// by the os/signal package.
//go:linkname signalWaitUntilIdle os/signal.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
// function, the state we are looking for is sigReceiving, not
// sigIdle. The sigIdle state is really more like sigProcessing.