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

os.File.Read() does not return after os.File.Close() and os.File.Fd() #26439

Closed
distributed opened this issue Jul 18, 2018 · 2 comments
Closed

Comments

@distributed
Copy link

What did you do?

Compile and run the following program.

package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"
)

func main() {
	err := Main()
	if err != nil {
		log.Fatal(err)
	}
}

func readpart(r io.ReadCloser) error {
	done := make(chan struct{})
	defer func() { <-done }()

	go func() {
		// To be reasonably sure that the parent goroutine is already
		// in the call to Read().
		time.Sleep(50 * time.Millisecond)
		err := r.Close()
		fmt.Printf("close err %v\n", err)
		done <- struct{}{}
	}()

	_, err := r.Read(make([]byte, 128))
	if err != nil {
		return err
	}

	return nil
}

func getfile() (*os.File, error) {
	r, _, err := os.Pipe()
	return r, err
}

func Main() error {
	f0, err := getfile()
	if err != nil {
		return err
	}

	err = readpart(f0)
	fmt.Printf("error of f0.Read(): %v\n", err)

	f1, err := getfile()
	if err != nil {
		return err
	}

	fmt.Printf("f1.Fd() %d\n", f1.Fd())

	err = readpart(f1)
	fmt.Printf("error of f1.Read(): %v\n", err)

	return nil
}

What did you expect to see?

The program exits with a successful error code. The program runs until the end because the Read() will return immediately after Close has been called on the underlying *os.File.

What did you see instead?

After f1.Close() has returned, the program blocks in a call to f1.Read().

Analysis with strace shows that close() is only called for f0. For f1, the Go stdlib never calls close(). From the side of my program, the only difference is that I called f1.Fd(), but not f0.Fd(). Remove the call to f1.Fd() and the program will terminate.

I am experiencing this issue while working with serial ports. In order to set the baud rate and other parameters, I need the file descriptor to deal with termios.

System details

go version go1.10.3 linux/amd64
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/joe/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/joe/gop"
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
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-build575248038=/tmp/go-build -gno-record-gcc-switches"
GOROOT/bin/go version: go version go1.10.3 linux/amd64
GOROOT/bin/go tool compile -V: compile version go1.10.3
uname -sr: Linux 4.13.0-45-generic
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.4 LTS
Release:	16.04
Codename:	xenial
/lib/x86_64-linux-gnu/libc.so.6: GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
gdb --version: GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
@distributed distributed changed the title os.File.Read() does not return after os.File.Read() os.File.Read() does not return after os.File.Close() and os.File.Fd() Jul 18, 2018
@ianlancetaylor
Copy link
Contributor

This is documented, somewhat cryptically, in the docs for the Fd method: "On Unix systems this will cause the SetDeadline methods to stop working." We've avoided a complete explanation in that method doc because it's complicated and lengthy and obscures the point of the method. To help clarify a little more, we recently expanded the docs for the Close method to add "On files that support SetDeadline, any pending I/O operations will be canceled and return immediately with an error." So putting these together, we see that if you call Fd the SetDeadline methods stop working, and then you no longer get the behavior you want from Close.

For your special purpose I recommend that you use syscall.Open, initialize the descriptor as needed, set it into non-blocking mode, and pass the descriptor to os.NewFile.

Closing this issue because I'm not sure there is anything we can change at this point.

@distributed
Copy link
Author

I missed this in the docs. Thank you for the clarification.

@golang golang locked and limited conversation to collaborators Jul 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants