Repository: go
Issue: runtime: signal handling: async signals should forward to existing C handlers by default 
Labels: compiler/runtime

`runtime.sigfwdgo` currently includes this snippet, intended to ignore signals per the [documented behavior of os/signal](https://golang.org/pkg/os/signal/#hdr-Changing_the_behavior_of_signals_in_Go_programs):
```go
	// If we aren't handling the signal, forward it.
	// […]
	if atomic.Load(&handlingSig[sig]) == 0 {
		sigfwd(fwdFn, sig, info, ctx)
		return true
	}

	flags := sigtable[sig].flags

	// Only forward synchronous signals and SIGPIPE.
	// […]
	if (c.sigcode() == _SI_USER || flags&_SigPanic == 0) && sig != _SIGPIPE {
		return false
	}
	// Determine if the signal occurred inside Go code. We test that:
	//   (1) we were in a goroutine (i.e., m.curg != nil), and
	//   (2) we weren't in CGO.
	g := getg()
	if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
		return false
	}
```

`handlingSig[sig]` is set for *all* of the signals that the `os/signal` package cares about any time `signal.Notify` is called for *any* signal.

That results in signals intended for C handlers (such as `SIGABRT`) being dropped, which is arguably incorrect for programs which may include C handlers for those signals.

----

For example, in the program below we register an early C handler for `SIGUSR1` and a Go handler for `SIGUSR2`, then signal the program with `SIGUSR1` (in a way that happens to ensure the signal is delivered to a thread in a Go stack frame, to make the program more deterministic).

Since there is a C handler for the signal and no corresponding `signal.Notify` call for that signal, `sigfwdgo` arguably ought to forward `SIGUSR1` to the C handler.

Instead, it returns without forwarding, the handler drops through to `runtime.sighandler`, and the signal is ignored.

```
bcmills:~/go$ go version
go version devel +228438e097 Wed Mar 8 21:34:32 2017 +0000 linux/amd64
```

src/asyncsig/asyncsig.go:
```go
package main

/*
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>

static volatile sig_atomic_t signaled = 0;

static void record_signal(int sig) {
	signaled = 1;
}

static void register_handler() __attribute__ ((constructor (200)));

static void register_handler() {
	signal(SIGUSR1, record_signal);
}

static bool was_signaled() {
	return signaled != 0;
}
*/
import "C"

import (
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

func main() {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGUSR2)

	tidc := make(chan int, 1)
	go func() {
		runtime.LockOSThread()
		tid := syscall.Gettid()
		for { // Loop forever sending the current TID.
			select {
			case tidc <- tid:
			default:
			}
		}
	}()

	syscall.Tgkill(syscall.Getpid(), <-tidc, syscall.SIGUSR1)

	// Wait for the thread to finish handling the signal.
	<-tidc
	<-tidc

	if !C.was_signaled() {
		panic("expected SIGUSR1 to have been signaled")
	}
}
```

```
bcmills:~$ go build asyncsig && ./asyncsig
panic: expected SIGUSR1 to have been signaled

goroutine 1 [running]:
main.main()
        /usr/local/google/home/bcmills/src/asyncsig/asyncsig.go:56 +0x1a3
```