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: codifying behavior of *os.File Close() more than once #20705

Open
joeshaw opened this Issue Jun 16, 2017 · 2 comments

Comments

Projects
None yet
5 participants
@joeshaw
Contributor

joeshaw commented Jun 16, 2017

(Current as of Go version 1.9 beta 1)

I wrote a blog post recently discussing whether it's appropriate to defer f.Close() on writable files. In discussion that followed afterward, there was the suggestion to both defer f.Close() to ensure that things were cleaned up, but also to explicitly to call f.Close() at the end and check errors there. I'm going to call this pattern the "double-close." It'd look something like this:

func helloWorld() error {
    f, err := os.Create("/tmp/notes.txt")
    if err != nil {
        return err
    }
    defer f.Close()

    if _, err io.WriteString(f, "hello world"); err != nil {
        return err
    }

    return f.Close()
}

In practice this works well, but it's not well-defined in the docs. The docs for io.Closer say, "The behavior of Close after the first call is undefined. Specific implementations may document their own behavior." The docs for *os.File Close() say nothing.

Under the covers, calling Close() first checks to see if the underlying file descriptor is -1 and returns syscall.EINVAL in that case. Otherwise it closes the file descriptor and sets it to -1. This makes it safe (though not idempotent) to call Close multiple times, and since it's deferred the second time, it's fine to discard the syscall.EINVAL error value.

From the discussion that followed my post, it would seem that this is already in fairly wide use in the wild. I would like for the documentation to call this fact out, as it'd suggest that the double-close is a valid practice and unlikely to break in the future.

@slrz

This comment has been minimized.

slrz commented Jun 17, 2017

I don't think this should be encouraged. People will cargo-cult the practice to other io.Closers which might not handle it well.

@joeshaw

This comment has been minimized.

Contributor

joeshaw commented Jun 26, 2017

My main counterpoint to that is that the io.Closer docs are already explicit about the behavior generally. My hope is that for implementations where calling it more than once is not safe, it would panic(), as doing so would indicate a programmer error. In most cases, though, I'd expect it to do what *os.File() does, which is return an implementation-specific error that can (usually) be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment