Don't defer Close() on writable files (2017)
131–140 of 311 posts
Re: Don't defer Close() on writable files (2017)
#132Earlier quoted context omitted.
Exceptions are a terrible error handling mechanism. You have no idea what throws and what doesn’t, it’s impossible to write defensive code that makes sense with exceptions. Errors as value is the only sane way to deal with errors. Granted, Go does it pretty badly but it’s still infinitely better than exceptions.
> You have no idea what throws and what doesn’t The answer here is that everything throws. Any code can have a Null/Nil dereference error, any code can use an array and generate an out-of-bounds exception, etc.
Re: Don't defer Close() on writable files (2017)
#133Earlier quoted context omitted.
So if I do fopen/fwrite/fsync/fclose, that is not enough? That is crazy, I think 90% of apps don't fsync the parent directory. Also, how many levels of parents do you need to fsync?
That should be only for creating files, and maybe updating their metadata (not sure about that one). The confusion stems from people thinking that files and directories are more different than they are. Both are inodes, and both are basically containers for data. File inodes are containers for actual data, while directory inodes are containers for other inodes. All inodes need to be fsynced when you write to them. Fo…
Uh? No.
You can imagine them as a list of name,inode tuples.
Re: Don't defer Close() on writable files (2017)
#134Earlier quoted context omitted.
I am talking about posix semantics yes, I have no idea how things work on windows.
Phew. I've primarily used Windows. Not that any of the posix programs I've been exposed to have done the dir sync though. For cross-platform stuff I've mainly used Boost, which I assumed handled such details.
Also these things are needed very very rarely (which is why few even know about the issue) and are not good for performance and battery life.
Re: Don't defer Close() on writable files (2017)
#135The snippet in the first update is very wrong. The manpage for Linux’s implementation of close() explicitly says that it should not be called again if it fails. Apparently, this is the same under FreeBSD.
https://pkg.go.dev/os#File.Close
“Close will return an error if it has already been called.”
An os.File is a data structure containing a file descriptor and some other fields. It is safe to call Close() multiple times, because it will only call the underlying syscall close() once.
Re: Don't defer Close() on writable files (2017)
#136TL;DR - defer Close ignores the error from Close, so don't.
func f(filename string) error {
fp, err := os.Create(filename)
if err != nil {
return err
}
defer fp.Close()
if _, err := fmt.Fprintln(fp, "Hello, world!"); err != nil {
return err
}
return fp.Close() // safe to call multiple times
}
The .Close() method is safe to call multiple times. This behavior is documented on the os.FileRe: Don't defer Close() on writable files (2017)
#137This is also how I'd solve it in Rust, except the defer would be implicit.
func doSomething() error {
f, err := os.Create("foo")
if err != nil { return err }
defer func(){ _ = f.Close() }()
if _, err := f.Write([]byte("bar"); err != nil { return err }
return f.Close()
}Re: Don't defer Close() on writable files (2017)
#138Re: Don't defer Close() on writable files (2017)
#139The end of the article has my favorite solution. This is also how I'd solve it in Rust, except the defer would be implicit. func doSomething() error { f, err := os.Create("foo") if err != nil { return err } defer func(){ _ = f.Close() }() if _, err := f.Write([]byte("bar"); err != nil { return err } return f.Close() }
defer f.Close()
Instead of, defer func(){ _ = f.Close() }()
I think this is likely a code style difference due to working with a linter that alarms on discarded error returns, but I’m not sure. Both options have the same behavior, unless you reassign f (defer f.Close() will use the original value, defer func() ... will use the current value).Re: Don't defer Close() on writable files (2017)
#140Earlier quoted context omitted.
That should be only for creating files, and maybe updating their metadata (not sure about that one). The confusion stems from people thinking that files and directories are more different than they are. Both are inodes, and both are basically containers for data. File inodes are containers for actual data, while directory inodes are containers for other inodes. All inodes need to be fsynced when you write to them. Fo…
> while directory inodes are containers for other inodes. Uh? No. You can imagine them as a list of name,inode tuples.