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

Comments

@mhagger
Copy link

@mhagger 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

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jun 3, 2019

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

This comment has been minimized.

Copy link

@gopherbot gopherbot commented Jun 3, 2019

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

@gopherbot gopherbot closed this in 0c75eb8 Jun 3, 2019
@mhagger

This comment has been minimized.

Copy link
Author

@mhagger mhagger commented Jun 3, 2019

Thanks so much for the quick response!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.