mirror of
https://github.com/golang/go
synced 2024-11-27 02:31:18 -07:00
runtime: don't always unblock all signals
Ian proposed an improved way of handling signals masks in Go, motivated by a problem where the Android java runtime expects certain signals to be blocked for all JVM threads. Discussion here https://groups.google.com/forum/#!topic/golang-dev/_TSCkQHJt6g Ian's text is used in the following: A Go program always needs to have the synchronous signals enabled. These are the signals for which _SigPanic is set in sigtable, namely SIGSEGV, SIGBUS, SIGFPE. A Go program that uses the os/signal package, and calls signal.Notify, needs to have at least one thread which is not blocking that signal, but it doesn't matter much which one. Unix programs do not change signal mask across execve. They inherit signal masks across fork. The shell uses this fact to some extent; for example, the job control signals (SIGTTIN, SIGTTOU, SIGTSTP) are blocked for commands run due to backquote quoting or $(). Our current position on signal masks was not thought out. We wandered into step by step, e.g., http://golang.org/cl/7323067 . This CL does the following: Introduce a new platform hook, msigsave, that saves the signal mask of the current thread to m.sigsave. Call msigsave from needm and newm. In minit grab set up the signal mask from m.sigsave and unblock the essential synchronous signals, and SIGILL, SIGTRAP, SIGPROF, SIGSTKFLT (for systems that have it). In unminit, restore the signal mask from m.sigsave. The first time that os/signal.Notify is called, start a new thread whose only purpose is to update its signal mask to make sure signals for signal.Notify are unblocked on at least one thread. The effect on Go programs will be that if they are invoked with some non-synchronous signals blocked, those signals will normally be ignored. Previously, those signals would mostly be ignored. A change in behaviour will occur for programs started with any of these signals blocked, if they receive the signal: SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM. Previously those signals would always cause a crash (unless using the os/signal package); with this change, they will be ignored if the program is started with the signal blocked (and does not use the os/signal package). ./all.bash completes successfully on linux/amd64. OpenBSD is missing the implementation. Change-Id: I188098ba7eb85eae4c14861269cc466f2aa40e8c Reviewed-on: https://go-review.googlesource.com/10173 Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
994b2d4645
commit
84cfba17c2
@ -10,3 +10,4 @@ func TestSetgid(t *testing.T) { testSetgid(t) }
|
||||
func Test6997(t *testing.T) { test6997(t) }
|
||||
func TestBuildID(t *testing.T) { testBuildID(t) }
|
||||
func Test9400(t *testing.T) { test9400(t) }
|
||||
func TestSigProcMask(t *testing.T) { testSigProcMask(t) }
|
||||
|
35
misc/cgo/test/sigprocmask_linux.c
Normal file
35
misc/cgo/test/sigprocmask_linux.c
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern void IntoGoAndBack();
|
||||
|
||||
int CheckBlocked() {
|
||||
sigset_t mask;
|
||||
sigprocmask(SIG_BLOCK, NULL, &mask);
|
||||
return sigismember(&mask, SIGIO);
|
||||
}
|
||||
|
||||
static void* sigthreadfunc(void* unused) {
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGIO);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
IntoGoAndBack();
|
||||
}
|
||||
|
||||
int RunSigThread() {
|
||||
pthread_t thread;
|
||||
int r;
|
||||
|
||||
r = pthread_create(&thread, NULL, &sigthreadfunc, NULL);
|
||||
if (r != 0)
|
||||
return r;
|
||||
return pthread_join(thread, NULL);
|
||||
}
|
38
misc/cgo/test/sigprocmask_linux.go
Normal file
38
misc/cgo/test/sigprocmask_linux.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cgotest
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -pthread
|
||||
#cgo LDFLAGS: -pthread
|
||||
extern int RunSigThread();
|
||||
extern int CheckBlocked();
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var blocked bool
|
||||
|
||||
//export IntoGoAndBack
|
||||
func IntoGoAndBack() {
|
||||
// Verify that SIGIO stays blocked on the C thread
|
||||
// even when unblocked for signal.Notify().
|
||||
signal.Notify(make(chan os.Signal), syscall.SIGIO)
|
||||
blocked = C.CheckBlocked() != 0
|
||||
}
|
||||
|
||||
func testSigProcMask(t *testing.T) {
|
||||
if r := C.RunSigThread(); r != 0 {
|
||||
t.Error("pthread_create/pthread_join failed")
|
||||
}
|
||||
if !blocked {
|
||||
t.Error("Go runtime unblocked SIGIO")
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ import "unsafe"
|
||||
|
||||
//extern SigTabTT runtime·sigtab[];
|
||||
|
||||
var sigset_none = uint32(0)
|
||||
var sigset_all = ^uint32(0)
|
||||
|
||||
func unimplemented(name string) {
|
||||
@ -126,17 +125,36 @@ func mpreinit(mp *m) {
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
|
||||
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
|
||||
throw("insufficient storage for signal mask")
|
||||
}
|
||||
sigprocmask(_SIG_SETMASK, nil, smask)
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
// Initialize signal handling.
|
||||
_g_ := getg()
|
||||
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
|
||||
sigprocmask(_SIG_SETMASK, &sigset_none, nil)
|
||||
|
||||
// restore signal mask from m.sigmask and unblock essential signals
|
||||
nmask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
nmask &^= 1 << (uint32(i) - 1)
|
||||
}
|
||||
}
|
||||
sigprocmask(_SIG_SETMASK, &nmask, nil)
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
_g_ := getg()
|
||||
smask := (*uint32)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
sigprocmask(_SIG_SETMASK, smask, nil)
|
||||
signalstack(nil, 0)
|
||||
}
|
||||
|
||||
@ -447,6 +465,6 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
sigprocmask(_SIG_SETMASK, &sigset_none, nil)
|
||||
func updatesigmask(m sigmask) {
|
||||
sigprocmask(_SIG_SETMASK, &m[0], nil)
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ const (
|
||||
_HW_NCPU = 3
|
||||
)
|
||||
|
||||
var sigset_none = sigset{}
|
||||
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
|
||||
|
||||
func getncpu() int32 {
|
||||
@ -120,6 +119,14 @@ func mpreinit(mp *m) {
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
|
||||
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
|
||||
throw("insufficient storage for signal mask")
|
||||
}
|
||||
sigprocmask(nil, smask)
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
@ -130,11 +137,22 @@ func minit() {
|
||||
|
||||
// Initialize signal handling
|
||||
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
|
||||
sigprocmask(&sigset_none, nil)
|
||||
|
||||
// restore signal mask from m.sigmask and unblock essential signals
|
||||
nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
|
||||
}
|
||||
}
|
||||
sigprocmask(&nmask, nil)
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
_g_ := getg()
|
||||
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
sigprocmask(smask, nil)
|
||||
signalstack(nil, 0)
|
||||
}
|
||||
|
||||
@ -215,6 +233,8 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
sigprocmask(&sigset_none, nil)
|
||||
func updatesigmask(m sigmask) {
|
||||
var mask sigset
|
||||
copy(mask.__bits[:], m[:])
|
||||
sigprocmask(&mask, nil)
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ const (
|
||||
_HW_NCPU = 3
|
||||
)
|
||||
|
||||
var sigset_none = sigset{}
|
||||
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
|
||||
|
||||
func getncpu() int32 {
|
||||
@ -119,6 +118,14 @@ func mpreinit(mp *m) {
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
|
||||
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
|
||||
throw("insufficient storage for signal mask")
|
||||
}
|
||||
sigprocmask(nil, smask)
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
@ -132,11 +139,22 @@ func minit() {
|
||||
|
||||
// Initialize signal handling.
|
||||
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
|
||||
sigprocmask(&sigset_none, nil)
|
||||
|
||||
// restore signal mask from m.sigmask and unblock essential signals
|
||||
nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
|
||||
}
|
||||
}
|
||||
sigprocmask(&nmask, nil)
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
_g_ := getg()
|
||||
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
sigprocmask(smask, nil)
|
||||
signalstack(nil, 0)
|
||||
}
|
||||
|
||||
@ -217,6 +235,8 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
sigprocmask(&sigset_none, nil)
|
||||
func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
|
||||
var mask sigset
|
||||
copy(mask.__bits[:], m[:])
|
||||
sigprocmask(&mask, nil)
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var sigset_none sigset
|
||||
var sigset_all sigset = sigset{^uint32(0), ^uint32(0)}
|
||||
|
||||
// Linux futex.
|
||||
@ -190,17 +189,36 @@ func mpreinit(mp *m) {
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
|
||||
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
|
||||
throw("insufficient storage for signal mask")
|
||||
}
|
||||
rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
// Initialize signal handling.
|
||||
_g_ := getg()
|
||||
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
|
||||
rtsigprocmask(_SIG_SETMASK, &sigset_none, nil, int32(unsafe.Sizeof(sigset_none)))
|
||||
|
||||
// restore signal mask from m.sigmask and unblock essential signals
|
||||
nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
nmask[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
|
||||
}
|
||||
}
|
||||
rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask)))
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
_g_ := getg()
|
||||
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
rtsigprocmask(_SIG_SETMASK, smask, nil, int32(unsafe.Sizeof(*smask)))
|
||||
signalstack(nil, 0)
|
||||
}
|
||||
|
||||
@ -304,6 +322,8 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
rtsigprocmask(_SIG_SETMASK, &sigset_none, nil, int32(unsafe.Sizeof(sigset_none)))
|
||||
func updatesigmask(m sigmask) {
|
||||
var mask sigset
|
||||
copy(mask[:], m[:])
|
||||
rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ func mpreinit(mp *m) {
|
||||
|
||||
func sigtramp()
|
||||
|
||||
func msigsave(mp *m) {
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
|
@ -17,7 +17,6 @@ const (
|
||||
_CLOCK_MONOTONIC = 3
|
||||
)
|
||||
|
||||
var sigset_none = sigset{}
|
||||
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
|
||||
|
||||
// From NetBSD's <sys/sysctl.h>
|
||||
@ -139,6 +138,14 @@ func mpreinit(mp *m) {
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
|
||||
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
|
||||
throw("insufficient storage for signal mask")
|
||||
}
|
||||
sigprocmask(_SIG_SETMASK, nil, smask)
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
@ -147,11 +154,23 @@ func minit() {
|
||||
|
||||
// Initialize signal handling
|
||||
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
|
||||
sigprocmask(_SIG_SETMASK, &sigset_none, nil)
|
||||
|
||||
// restore signal mask from m.sigmask and unblock essential signals
|
||||
nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
|
||||
}
|
||||
}
|
||||
sigprocmask(_SIG_SETMASK, &nmask, nil)
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
_g_ := getg()
|
||||
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
sigprocmask(_SIG_SETMASK, smask, nil)
|
||||
|
||||
signalstack(nil, 0)
|
||||
}
|
||||
|
||||
@ -206,6 +225,8 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
sigprocmask(_SIG_SETMASK, &sigset_none, nil)
|
||||
func updatesigmask(m sigmask) {
|
||||
var mask sigset
|
||||
copy(mask.__bits[:], m[:])
|
||||
sigprocmask(_SIG_SETMASK, &mask, nil)
|
||||
}
|
||||
|
@ -148,6 +148,9 @@ func mpreinit(mp *m) {
|
||||
mp.gsignal.m = mp
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
@ -217,6 +220,6 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
sigprocmask(_SIG_SETMASK, sigset_none)
|
||||
func updatesigmask(m sigmask) {
|
||||
sigprocmask(_SIG_SETMASK, m[0])
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ func mpreinit(mp *m) {
|
||||
mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, _FlagNoScan))
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
|
@ -292,6 +292,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
|
||||
func mpreinit(mp *m) {
|
||||
}
|
||||
|
||||
func msigsave(mp *m) {
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
|
@ -114,7 +114,6 @@ var (
|
||||
libc_write libcFunc
|
||||
)
|
||||
|
||||
var sigset_none = sigset{}
|
||||
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
|
||||
|
||||
func getncpu() int32 {
|
||||
@ -190,6 +189,14 @@ func mpreinit(mp *m) {
|
||||
|
||||
func miniterrno()
|
||||
|
||||
func msigsave(mp *m) {
|
||||
smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
|
||||
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
|
||||
throw("insufficient storage for signal mask")
|
||||
}
|
||||
sigprocmask(_SIG_SETMASK, nil, smask)
|
||||
}
|
||||
|
||||
// Called to initialize a new m (including the bootstrap m).
|
||||
// Called on the new thread, can not allocate memory.
|
||||
func minit() {
|
||||
@ -197,11 +204,23 @@ func minit() {
|
||||
asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno))
|
||||
// Initialize signal handling
|
||||
signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
|
||||
sigprocmask(_SIG_SETMASK, &sigset_none, nil)
|
||||
|
||||
// restore signal mask from m.sigmask and unblock essential signals
|
||||
nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
nmask.__sigbits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
|
||||
}
|
||||
}
|
||||
sigprocmask(_SIG_SETMASK, &nmask, nil)
|
||||
}
|
||||
|
||||
// Called from dropm to undo the effect of an minit.
|
||||
func unminit() {
|
||||
_g_ := getg()
|
||||
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
|
||||
sigprocmask(_SIG_SETMASK, smask, nil)
|
||||
|
||||
signalstack(nil, 0)
|
||||
}
|
||||
|
||||
@ -278,8 +297,10 @@ func signalstack(p *byte, n int32) {
|
||||
sigaltstack(&st, nil)
|
||||
}
|
||||
|
||||
func unblocksignals() {
|
||||
sigprocmask(_SIG_SETMASK, &sigset_none, nil)
|
||||
func updatesigmask(m sigmask) {
|
||||
var mask sigset
|
||||
copy(mask.__sigbits[:], m[:])
|
||||
sigprocmask(_SIG_SETMASK, &mask, nil)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
|
@ -944,6 +944,7 @@ func needm(x byte) {
|
||||
_g_.stack.lo = uintptr(noescape(unsafe.Pointer(&x))) - 32*1024
|
||||
_g_.stackguard0 = _g_.stack.lo + _StackGuard
|
||||
|
||||
msigsave(mp)
|
||||
// Initialize this thread to use the m.
|
||||
asminit()
|
||||
minit()
|
||||
@ -1071,6 +1072,7 @@ func unlockextra(mp *m) {
|
||||
func newm(fn func(), _p_ *p) {
|
||||
mp := allocm(_p_, fn)
|
||||
mp.nextp.set(_p_)
|
||||
msigsave(mp)
|
||||
if iscgo {
|
||||
var ts cgothreadstart
|
||||
if _cgo_thread_start == nil {
|
||||
|
@ -266,6 +266,7 @@ type m struct {
|
||||
// Fields not known to debuggers.
|
||||
procid uint64 // for debuggers, but offset not hard-coded
|
||||
gsignal *g // signal-handling g
|
||||
sigmask [4]uintptr // storage for saved signal mask
|
||||
tls [4]uintptr // thread-local storage (for x86 extern register)
|
||||
mstartfn func()
|
||||
curg *g // current running goroutine
|
||||
@ -469,15 +470,16 @@ type sigtabtt struct {
|
||||
}
|
||||
|
||||
const (
|
||||
_SigNotify = 1 << 0 // let signal.Notify have signal, even if from kernel
|
||||
_SigKill = 1 << 1 // if signal.Notify doesn't take it, exit quietly
|
||||
_SigThrow = 1 << 2 // if signal.Notify doesn't take it, exit loudly
|
||||
_SigPanic = 1 << 3 // if the signal is from the kernel, panic
|
||||
_SigDefault = 1 << 4 // if the signal isn't explicitly requested, don't monitor it
|
||||
_SigHandling = 1 << 5 // our signal handler is registered
|
||||
_SigIgnored = 1 << 6 // the signal was ignored before we registered for it
|
||||
_SigGoExit = 1 << 7 // cause all runtime procs to exit (only used on Plan 9).
|
||||
_SigSetStack = 1 << 8 // add SA_ONSTACK to libc handler
|
||||
_SigNotify = 1 << iota // let signal.Notify have signal, even if from kernel
|
||||
_SigKill // if signal.Notify doesn't take it, exit quietly
|
||||
_SigThrow // if signal.Notify doesn't take it, exit loudly
|
||||
_SigPanic // if the signal is from the kernel, panic
|
||||
_SigDefault // if the signal isn't explicitly requested, don't monitor it
|
||||
_SigHandling // our signal handler is registered
|
||||
_SigIgnored // the signal was ignored before we registered for it
|
||||
_SigGoExit // cause all runtime procs to exit (only used on Plan 9).
|
||||
_SigSetStack // add SA_ONSTACK to libc handler
|
||||
_SigUnblock // unblocked in minit
|
||||
)
|
||||
|
||||
// Layout of in-memory per-function information prepared by linker
|
||||
|
@ -19,6 +19,19 @@ const (
|
||||
// Signal forwarding is currently available only on Linux.
|
||||
var fwdSig [_NSIG]uintptr
|
||||
|
||||
// sigmask represents a general signal mask compatible with the GOOS
|
||||
// specific sigset types: the signal numbered x is represented by bit x-1
|
||||
// to match the representation expected by sigprocmask.
|
||||
type sigmask [(_NSIG + 31) / 32]uint32
|
||||
|
||||
// channels for synchronizing signal mask updates with the signal mask
|
||||
// thread
|
||||
var (
|
||||
disableSigChan chan uint32
|
||||
enableSigChan chan uint32
|
||||
maskUpdatedChan chan struct{}
|
||||
)
|
||||
|
||||
func initsig() {
|
||||
// _NSIG is the number of signals on this operating system.
|
||||
// sigtable should describe what to do for all the possible signals.
|
||||
@ -61,13 +74,18 @@ func sigenable(sig uint32) {
|
||||
}
|
||||
|
||||
t := &sigtable[sig]
|
||||
if t.flags&_SigNotify != 0 && t.flags&_SigHandling == 0 {
|
||||
if t.flags&_SigNotify != 0 {
|
||||
ensureSigM()
|
||||
enableSigChan <- sig
|
||||
<-maskUpdatedChan
|
||||
if t.flags&_SigHandling == 0 {
|
||||
t.flags |= _SigHandling
|
||||
if getsig(int32(sig)) == _SIG_IGN {
|
||||
t.flags |= _SigIgnored
|
||||
}
|
||||
setsig(int32(sig), funcPC(sighandler), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sigdisable(sig uint32) {
|
||||
@ -76,7 +94,11 @@ func sigdisable(sig uint32) {
|
||||
}
|
||||
|
||||
t := &sigtable[sig]
|
||||
if t.flags&_SigNotify != 0 && t.flags&_SigHandling != 0 {
|
||||
if t.flags&_SigNotify != 0 {
|
||||
ensureSigM()
|
||||
disableSigChan <- sig
|
||||
<-maskUpdatedChan
|
||||
if t.flags&_SigHandling != 0 {
|
||||
t.flags &^= _SigHandling
|
||||
if t.flags&_SigIgnored != 0 {
|
||||
setsig(int32(sig), _SIG_IGN, true)
|
||||
@ -84,6 +106,7 @@ func sigdisable(sig uint32) {
|
||||
setsig(int32(sig), _SIG_DFL, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sigignore(sig uint32) {
|
||||
@ -130,7 +153,52 @@ func crash() {
|
||||
}
|
||||
}
|
||||
|
||||
unblocksignals()
|
||||
updatesigmask(sigmask{})
|
||||
setsig(_SIGABRT, _SIG_DFL, false)
|
||||
raise(_SIGABRT)
|
||||
}
|
||||
|
||||
// createSigM starts one global, sleeping thread to make sure at least one thread
|
||||
// is available to catch signals enabled for os/signal.
|
||||
func ensureSigM() {
|
||||
if maskUpdatedChan != nil {
|
||||
return
|
||||
}
|
||||
maskUpdatedChan = make(chan struct{})
|
||||
disableSigChan = make(chan uint32)
|
||||
enableSigChan = make(chan uint32)
|
||||
go func() {
|
||||
// Signal masks are per-thread, so make sure this goroutine stays on one
|
||||
// thread.
|
||||
LockOSThread()
|
||||
defer UnlockOSThread()
|
||||
// The sigBlocked mask contains the signals not active for os/signal,
|
||||
// initially all signals except the essential. When signal.Notify()/Stop is called,
|
||||
// sigenable/sigdisable in turn notify this thread to update its signal
|
||||
// mask accordingly.
|
||||
var sigBlocked sigmask
|
||||
for i := range sigBlocked {
|
||||
sigBlocked[i] = ^uint32(0)
|
||||
}
|
||||
for i := range sigtable {
|
||||
if sigtable[i].flags&_SigUnblock != 0 {
|
||||
sigBlocked[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
|
||||
}
|
||||
}
|
||||
updatesigmask(sigBlocked)
|
||||
for {
|
||||
select {
|
||||
case sig := <-enableSigChan:
|
||||
if b := sig - 1; b >= 0 {
|
||||
sigBlocked[b/32] &^= (1 << (b & 31))
|
||||
}
|
||||
case sig := <-disableSigChan:
|
||||
if b := sig - 1; b >= 0 {
|
||||
sigBlocked[b/32] |= (1 << (b & 31))
|
||||
}
|
||||
}
|
||||
updatesigmask(sigBlocked)
|
||||
maskUpdatedChan <- struct{}{}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -16,14 +16,14 @@ var sigtable = [...]sigTabT{
|
||||
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
|
||||
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
|
||||
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
|
||||
/* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
|
||||
/* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
|
||||
/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
|
||||
/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
|
||||
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
|
||||
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
|
||||
/* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
|
||||
/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
|
||||
/* 9 */ {0, "SIGKILL: kill"},
|
||||
/* 10 */ {_SigPanic, "SIGBUS: bus error"},
|
||||
/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
|
||||
/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
|
||||
/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
|
||||
/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
|
||||
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
|
||||
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
|
||||
@ -32,14 +32,14 @@ var sigtable = [...]sigTabT{
|
||||
/* 17 */ {0, "SIGSTOP: stop"},
|
||||
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
|
||||
/* 19 */ {0, "SIGCONT: continue after stop"},
|
||||
/* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
|
||||
/* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
|
||||
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
|
||||
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
|
||||
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
|
||||
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
|
||||
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
|
||||
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
|
||||
/* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
|
||||
/* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
|
||||
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
|
||||
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
|
||||
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
|
||||
|
@ -16,20 +16,20 @@ var sigtable = [...]sigTabT{
|
||||
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
|
||||
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
|
||||
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
|
||||
/* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
|
||||
/* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
|
||||
/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
|
||||
/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
|
||||
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
|
||||
/* 7 */ {_SigPanic, "SIGBUS: bus error"},
|
||||
/* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
|
||||
/* 7 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
|
||||
/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
|
||||
/* 9 */ {0, "SIGKILL: kill"},
|
||||
/* 10 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
|
||||
/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
|
||||
/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
|
||||
/* 12 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
|
||||
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
|
||||
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
|
||||
/* 15 */ {_SigNotify + _SigKill, "SIGTERM: termination"},
|
||||
/* 16 */ {_SigThrow, "SIGSTKFLT: stack fault"},
|
||||
/* 17 */ {_SigNotify, "SIGCHLD: child status has changed"},
|
||||
/* 16 */ {_SigThrow + _SigUnblock, "SIGSTKFLT: stack fault"},
|
||||
/* 17 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
|
||||
/* 18 */ {0, "SIGCONT: continue"},
|
||||
/* 19 */ {0, "SIGSTOP: stop, unblockable"},
|
||||
/* 20 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
|
||||
@ -39,7 +39,7 @@ var sigtable = [...]sigTabT{
|
||||
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
|
||||
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
|
||||
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
|
||||
/* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
|
||||
/* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
|
||||
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
|
||||
/* 29 */ {_SigNotify, "SIGIO: i/o now possible"},
|
||||
/* 30 */ {_SigNotify, "SIGPWR: power failure restart"},
|
||||
|
@ -14,14 +14,14 @@ var sigtable = [...]sigTabT{
|
||||
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
|
||||
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
|
||||
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
|
||||
/* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
|
||||
/* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
|
||||
/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
|
||||
/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
|
||||
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
|
||||
/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
|
||||
/* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
|
||||
/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
|
||||
/* 9 */ {0, "SIGKILL: kill"},
|
||||
/* 10 */ {_SigPanic, "SIGBUS: bus error"},
|
||||
/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
|
||||
/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
|
||||
/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
|
||||
/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
|
||||
/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
|
||||
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
|
||||
@ -30,14 +30,14 @@ var sigtable = [...]sigTabT{
|
||||
/* 17 */ {0, "SIGSTOP: stop"},
|
||||
/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
|
||||
/* 19 */ {0, "SIGCONT: continue after stop"},
|
||||
/* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
|
||||
/* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
|
||||
/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
|
||||
/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
|
||||
/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
|
||||
/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
|
||||
/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
|
||||
/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
|
||||
/* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
|
||||
/* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
|
||||
/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
|
||||
/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
|
||||
/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
|
||||
|
@ -14,21 +14,21 @@ var sigtable = [...]sigTabT{
|
||||
/* 1 */ {_SigNotify + _SigKill, "SIGHUP: hangup"},
|
||||
/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt (rubout)"},
|
||||
/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit (ASCII FS)"},
|
||||
/* 4 */ {_SigThrow, "SIGILL: illegal instruction (not reset when caught)"},
|
||||
/* 5 */ {_SigThrow, "SIGTRAP: trace trap (not reset when caught)"},
|
||||
/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction (not reset when caught)"},
|
||||
/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap (not reset when caught)"},
|
||||
/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: used by abort, replace SIGIOT in the future"},
|
||||
/* 7 */ {_SigThrow, "SIGEMT: EMT instruction"},
|
||||
/* 8 */ {_SigPanic, "SIGFPE: floating point exception"},
|
||||
/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating point exception"},
|
||||
/* 9 */ {0, "SIGKILL: kill (cannot be caught or ignored)"},
|
||||
/* 10 */ {_SigPanic, "SIGBUS: bus error"},
|
||||
/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
|
||||
/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
|
||||
/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
|
||||
/* 12 */ {_SigThrow, "SIGSYS: bad argument to system call"},
|
||||
/* 13 */ {_SigNotify, "SIGPIPE: write on a pipe with no one to read it"},
|
||||
/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
|
||||
/* 15 */ {_SigNotify + _SigKill, "SIGTERM: software termination signal from kill"},
|
||||
/* 16 */ {_SigNotify, "SIGUSR1: user defined signal 1"},
|
||||
/* 17 */ {_SigNotify, "SIGUSR2: user defined signal 2"},
|
||||
/* 18 */ {_SigNotify, "SIGCHLD: child status change alias (POSIX)"},
|
||||
/* 18 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status change alias (POSIX)"},
|
||||
/* 19 */ {_SigNotify, "SIGPWR: power-fail restart"},
|
||||
/* 20 */ {_SigNotify, "SIGWINCH: window size change"},
|
||||
/* 21 */ {_SigNotify, "SIGURG: urgent socket condition"},
|
||||
@ -39,7 +39,7 @@ var sigtable = [...]sigTabT{
|
||||
/* 26 */ {_SigNotify + _SigDefault, "SIGTTIN: background tty read attempted"},
|
||||
/* 27 */ {_SigNotify + _SigDefault, "SIGTTOU: background tty write attempted"},
|
||||
/* 28 */ {_SigNotify, "SIGVTALRM: virtual timer expired"},
|
||||
/* 29 */ {_SigNotify, "SIGPROF: profiling timer expired"},
|
||||
/* 29 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling timer expired"},
|
||||
/* 30 */ {_SigNotify, "SIGXCPU: exceeded cpu limit"},
|
||||
/* 31 */ {_SigNotify, "SIGXFSZ: exceeded file size limit"},
|
||||
/* 32 */ {_SigNotify, "SIGWAITING: reserved signal no longer used by"},
|
||||
|
Loading…
Reference in New Issue
Block a user