Named pipes with O_NONBLOCK still block

Hello! I think I’ve found a bug with go 1.22.1 on an M1 mac and I’d love to know if anyone knows what is going on. When I try to read from a named pipe with O_NONBLOCK, then calls to Read() still block after the pipe is closed.

This only happens when I write to the pipe slowly, using time.Sleep(). This is my writer:

go func() {
	pipe, _ := os.OpenFile("p.pipe", os.O_WRONLY|os.O_APPEND, os.ModeNamedPipe)
	for range 5 {
		pipe.WriteString("Hello\n")
		// Removing this Sleep() does not cause this bug
		time.Sleep(1000 * time.Millisecond)
	}
	err := pipe.Close()
	fmt.Println("Writer closed pipe", err)
}()

And my reader:

pipe, _ := os.OpenFile("p.pipe", os.O_RDONLY|syscall.O_NONBLOCK, os.ModeNamedPipe)

buf := make([]byte, 1)
for {
	// This should loop infinitely, even after the writer closes the pipe
	// However, after the pipe is closed, Read() blocks
	n, err := pipe.Read(buf)
	fmt.Println("Read from pipe", n, err)
}

The full program is here: Named pipes with O_NONBLOCK still block · GitHub

I’m running go version go1.22.1 darwin/arm64; M1 2020 macbook with OS version 12.5 (21G72)

You’re right, using O_NONBLOCK with named pipes (FIFOs) can be a bit counterintuitive. Here’s why:

O_NONBLOCK with Named Pipes:

  • When you open a named pipe with O_NONBLOCK, it doesn’t prevent blocking on all operations.
  • It only makes read() and write() calls non-blocking.
  • If there’s no data to read or the pipe’s buffer is full for writing, these calls will return -1 with errno set to EAGAIN or EWOULDBLOCK.

Thanks! I believe we are in agreement here. My observation - my bug report - is that with my example program, the Read() call does indeed block even though it should be non-blocking. I believe this is a bug in Go’s implementation for Mac. When I run the same program on Linux, the read call behaves in a non-blocking way as expected.

Turns out this is a bug in go. os: Read() blocking on named pipe despite O_NONBLOCK · Issue #66239 · golang/go · GitHub