| // 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 <stddef.h> |
| #include <signal.h> |
| #include <pthread.h> |
| |
| // 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" |
| "io/fs" |
| "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 { |
| // An overloaded system |
| // may fail with EAGAIN. |
| // This doesn't tell us |
| // anything useful; ignore it. |
| // Issue #27731. |
| if isEAGAIN(err) { |
| return |
| } |
| fmt.Printf("iteration %d: %v\n", j, err) |
| os.Exit(1) |
| } |
| }(j) |
| signal.Stop(c2) |
| } |
| }() |
| } |
| wg.Wait() |
| |
| fmt.Println("OK") |
| } |
| |
| // isEAGAIN reports whether err is an EAGAIN error from a process execution. |
| func isEAGAIN(err error) bool { |
| if p, ok := err.(*fs.PathError); ok { |
| err = p.Err |
| } |
| return err == syscall.EAGAIN |
| } |