Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: signal.Ignore(syscall.SIGPIPE) does not work as documented #32386

Closed
mhagger opened this issue Jun 2, 2019 · 3 comments
Closed

runtime: signal.Ignore(syscall.SIGPIPE) does not work as documented #32386

mhagger opened this issue Jun 2, 2019 · 3 comments
Labels
FrozenDueToAge help wanted NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@mhagger
Copy link

mhagger commented Jun 2, 2019

Based on the documentation, calling signal.Ignore(syscall.SIGPIPE) should cause SIGPIPE to be ignored. But that is not the case.

What version of Go are you using (go version)?

$ go version
go version go1.12.5 linux/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/mhagger/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/mhagger/.local/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build772110767=/tmp/go-build -gno-record-gcc-switches"

What did you do?

The following program calls signal.Ignore() to try to ignore SIGPIPE:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	signal.Ignore(syscall.SIGPIPE)
	// Give the child time to terminate:
	time.Sleep(1 * time.Second)
	_, err := fmt.Printf("Hello, world!\n")
	if err == nil {
		fmt.Fprintf(os.Stderr, "Printf succeeded\n")
	} else {
		fmt.Fprintf(os.Stderr, "Printf failed with %v\n", err)
	}
	// Allow time for any possible signals to be handled:
	time.Sleep(1 * time.Second)
	fmt.Fprintf(os.Stderr, "Bye!\n")
}

But when the program is run with stdout piped into a process that terminates quickly, the resulting SIGPIPE signal causes it to die:

$ go run t.go | /bin/true
signal: broken pipe

What did you expect to see?

I expected the program not be killed by SIGPIPE, and to report that Printf failed with EPIPE. This expectation is based on the documentation for signal.Ignore() (especially the second sentence):

Ignore causes the provided signals to be ignored. If they are received by the program, nothing will happen. Ignore undoes the effect of any prior calls to Notify for the provided signals. If no signals are provided, all incoming signals will be ignored.

My expectation is also consistent with the description of how SIGPIPE is handled](https://golang.org/pkg/os/signal/#hdr-SIGPIPE).

What did you see instead?

The program was killed by SIGPIPE.

Discussion

The above commands were run using bash. I also tried under dash, with the same results.

If the original program is built in a separate step and then the binary is run, it doesn't print "signal: broken pipe" but it still dies after only one second and without printing the subsequent information to stderr:

$ go build -o t t.go
$ ./t | /bin/true
$ time ./t | /bin/true

real	0m1.006s
user	0m0.000s
sys	0m0.000s

Contrast that with running the same program using go run, as above:

$ time go run t.go | /bin/true
signal: broken pipe

real	0m1.124s
user	0m0.136s
sys	0m0.020s

Note that it also completes in just over a second, but this time with the signal: broken pipe message.

Note that it is possible to cause SIGPIPE to really be ignored by calling

signal.Notify(make(chan os.Signal), syscall.SIGPIPE)

But if you follow that call with another call to signal.Ignore(syscall.SIGPIPE), the signal again fails to be ignored.

@ianlancetaylor ianlancetaylor added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. and removed Documentation labels Jun 3, 2019
@ianlancetaylor ianlancetaylor added this to the Go1.14 milestone Jun 3, 2019
@ianlancetaylor
Copy link
Contributor

The fix is probably to have the sigpipe function in runtime/signal_unix.go just return if signal_ignored(SIGPIPE) reports true.

@ianlancetaylor ianlancetaylor changed the title signal: signal.Ignore(syscall.SIGPIPE) does not work as documented runtime: signal.Ignore(syscall.SIGPIPE) does not work as documented Jun 3, 2019
@gopherbot
Copy link

Change https://golang.org/cl/180177 mentions this issue: runtime: fix sigpipe do not check SIGPIPE was ignored

@mhagger
Copy link
Author

mhagger commented Jun 3, 2019

Thanks so much for the quick response! ✨

@golang golang locked and limited conversation to collaborators Jun 2, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge help wanted NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

3 participants