diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 346a80f0ae..9422a08620 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -121,3 +121,16 @@ func TestCgoDLLImports(t *testing.T) { t.Fatalf("expected %q, but got %v", want, got) } } + +func TestCgoExecSignalMask(t *testing.T) { + // Test issue 13164. + switch runtime.GOOS { + case "windows", "plan9": + t.Skipf("skipping signal mask test on %s", runtime.GOOS) + } + got := runTestProg(t, "testprogcgo", "CgoExecSignalMask") + want := "OK\n" + if got != want { + t.Errorf("expected %q, got %v", want, got) + } +} diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 12fba1404c..fd8e161c17 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -98,6 +98,9 @@ func main_main() // runtimeInitTime is the nanotime() at which the runtime started. var runtimeInitTime int64 +// Value to use for signal mask for newly created M's. +var initSigmask sigset + // The main goroutine. func main() { g := getg() @@ -430,6 +433,9 @@ func schedinit() { mallocinit() mcommoninit(_g_.m) + msigsave(_g_.m) + initSigmask = _g_.m.sigmask + goargs() goenvs() parsedebugvars() @@ -1480,7 +1486,7 @@ func unlockextra(mp *m) { func newm(fn func(), _p_ *p) { mp := allocm(_p_, fn) mp.nextp.set(_p_) - msigsave(mp) + mp.sigmask = initSigmask if iscgo { var ts cgothreadstart if _cgo_thread_start == nil { diff --git a/src/runtime/testdata/testprogcgo/exec.go b/src/runtime/testdata/testprogcgo/exec.go new file mode 100644 index 0000000000..8dc1d517c6 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/exec.go @@ -0,0 +1,89 @@ +// 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. + +// +build !plan9,!windows + +package main + +/* +#include +#include +#include + +// Save the signal mask at startup so that we see what it is before +// the Go runtime starts setting up signals. + +static sigset_t mask; + +static void init(void) __attribute__ ((constructor)); + +static void init() { + sigemptyset(&mask); + pthread_sigmask(SIG_SETMASK, NULL, &mask); +} + +int SIGINTBlocked() { + return sigismember(&mask, SIGINT); +} +*/ +import "C" + +import ( + "fmt" + "os" + "os/exec" + "os/signal" + "sync" + "syscall" +) + +func init() { + register("CgoExecSignalMask", CgoExecSignalMask) +} + +func CgoExecSignalMask() { + if len(os.Args) > 2 && os.Args[2] == "testsigint" { + if C.SIGINTBlocked() != 0 { + os.Exit(1) + } + os.Exit(0) + } + + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGTERM) + go func() { + for range c { + } + }() + + const goCount = 10 + const execCount = 10 + var wg sync.WaitGroup + wg.Add(goCount*execCount + goCount) + for i := 0; i < goCount; i++ { + go func() { + defer wg.Done() + for j := 0; j < execCount; j++ { + c2 := make(chan os.Signal, 1) + signal.Notify(c2, syscall.SIGUSR1) + syscall.Kill(os.Getpid(), syscall.SIGTERM) + go func(j int) { + defer wg.Done() + cmd := exec.Command(os.Args[0], "CgoExecSignalMask", "testsigint") + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Printf("iteration %d: %v\n", j, err) + os.Exit(1) + } + }(j) + signal.Stop(c2) + } + }() + } + wg.Wait() + + fmt.Println("OK") +}