]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't always unblock all signals
authorElias Naur <elias.naur@gmail.com>
Mon, 18 May 2015 09:00:24 +0000 (11:00 +0200)
committerIan Lance Taylor <iant@golang.org>
Fri, 22 May 2015 20:24:08 +0000 (20:24 +0000)
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>
20 files changed:
misc/cgo/test/cgo_linux_test.go
misc/cgo/test/sigprocmask_linux.c [new file with mode: 0644]
misc/cgo/test/sigprocmask_linux.go [new file with mode: 0644]
src/runtime/os1_darwin.go
src/runtime/os1_dragonfly.go
src/runtime/os1_freebsd.go
src/runtime/os1_linux.go
src/runtime/os1_nacl.go
src/runtime/os1_netbsd.go
src/runtime/os1_openbsd.go
src/runtime/os1_plan9.go
src/runtime/os1_windows.go
src/runtime/os3_solaris.go
src/runtime/proc1.go
src/runtime/runtime2.go
src/runtime/signal1_unix.go
src/runtime/signal_darwin.go
src/runtime/signal_linux.go
src/runtime/signal_netbsd.go
src/runtime/signal_solaris.go

index 6e1d1065f63bbcca821c79267743f1a1c718f2c6..3cc2af5919c21ac31d55376afbaccee41c87926b 100644 (file)
@@ -6,7 +6,8 @@ package cgotest
 
 import "testing"
 
-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 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) }
diff --git a/misc/cgo/test/sigprocmask_linux.c b/misc/cgo/test/sigprocmask_linux.c
new file mode 100644 (file)
index 0000000..6597e98
--- /dev/null
@@ -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);
+}
diff --git a/misc/cgo/test/sigprocmask_linux.go b/misc/cgo/test/sigprocmask_linux.go
new file mode 100644 (file)
index 0000000..7d343e9
--- /dev/null
@@ -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")
+       }
+}
index 10cf460f7fe69b3af19881a14030287572fd5ed2..1b74e3e6536c6995762799f63c0e0001d1adaa12 100644 (file)
@@ -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)
 }
index a590aea39be49fbf7d9735a2a3ab12acbe553ae0..eb42b54e2b96a2910072cde0280825c95ff50bc8 100644 (file)
@@ -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)
 }
index 8719a49286839d12e1a256da896e95420832ca98..f7f34bd38685e31ffd027cf5fc1e794a4747e8cc 100644 (file)
@@ -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)
 }
index e4b18c79b314b291f75328046729d9e97fc60ab7..02f98d7c5fdbbc1671d651d33e2584ecea02fb4d 100644 (file)
@@ -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)))
 }
index dbb5dec2fdc8a675f1ae7df9322f765bf1fdb7fb..66e60f8b1222205d301860687d6d381fdb51949f 100644 (file)
@@ -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() {
index 8df74b5593c8f73d5b058a26d3556dbac91a45bb..3fb05989e7e2ab88d27cd1f5f8fe7dff4e79d6eb 100644 (file)
@@ -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)
 }
index 95729a56df85035a94980b610d1c2888507d671c..98af545f7fbed294d07727c9a4389c3fe92a2498 100644 (file)
@@ -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])
 }
index 1aae96a999acef300c807e84eb67ea321a788bf8..bda7057f4493481f73b79e226df437c3691aec00 100644 (file)
@@ -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() {
index 5719b320f52953b78ed7c61dbc6aa324e7106c69..bc472d0de99de2a1c4ad4af6a2c53506c649fdce 100644 (file)
@@ -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() {
index 69ac5b49708ebb0d566292a34881e280c50ca494..e4fe92de4165a21b5be9dc536029d0ae98077514 100644 (file)
@@ -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
index 27281406b883ddb8b41352da1101b12093290b6e..c070f7d773dac67eed1b5ed097121029136c9210 100644 (file)
@@ -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 {
index 83d8062baf97308cb7cb9ad6d265002a9a179f70..3ee5d5d29de7d750ebe2ae0f2e7f6268b03e0462 100644 (file)
@@ -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
index 7577d43a64d2fde24e320825f5bfdad0cf26b0d8..d3e9dac09797762eea5832892bc7cead4b82a5f6 100644 (file)
@@ -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,12 +74,17 @@ func sigenable(sig uint32) {
        }
 
        t := &sigtable[sig]
-       if t.flags&_SigNotify != 0 && t.flags&_SigHandling == 0 {
-               t.flags |= _SigHandling
-               if getsig(int32(sig)) == _SIG_IGN {
-                       t.flags |= _SigIgnored
+       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)
                }
-               setsig(int32(sig), funcPC(sighandler), true)
        }
 }
 
@@ -76,12 +94,17 @@ func sigdisable(sig uint32) {
        }
 
        t := &sigtable[sig]
-       if t.flags&_SigNotify != 0 && t.flags&_SigHandling != 0 {
-               t.flags &^= _SigHandling
-               if t.flags&_SigIgnored != 0 {
-                       setsig(int32(sig), _SIG_IGN, true)
-               } else {
-                       setsig(int32(sig), _SIG_DFL, true)
+       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)
+                       } else {
+                               setsig(int32(sig), _SIG_DFL, true)
+                       }
                }
        }
 }
@@ -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{}{}
+               }
+       }()
+}
index 32ecce0d7dfbf006ad00cb159960ecd63ddac7d4..6cd18653d5f0c88509ee4f486c1002b729ae5853 100644 (file)
@@ -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"},
index f8250b9fa1111eaaabb30dfc904937fa3920c8f3..2f25b59663d35108e26668c43e0ac1c7b30c70b4 100644 (file)
@@ -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"},
index 78afc59efacd97423faee7f9f4ac04da052a0783..d93a450d987c30a2a0cdaad00f5107354e1a5b93 100644 (file)
@@ -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"},
index 2986c5aabcb8f771e7809216e6ee2204be50294b..d8ac67684640331b1390290e88ddcddee1394a2b 100644 (file)
@@ -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"},