2 ms·
You could go the other way: the program is incorrect if it doesn't explicitly close files. Allowing incorrect programs to somehow work correctly only encourages
by chowells 3y ago
You could go the other way: the program is incorrect if it doesn't explicitly close files. Allowing incorrect programs to somehow work correctly only encourages programmers to create more advanced incorrect programs, until they exceed the ability of the language/runtime to fix.
I tend more towards this line of thought. But I'd still put a finalizer on file handles. I just would have it yell very loudly that there's a bug. Maybe even close the file, but there's no way I wouldn't at the very least be generating a lot of diagnostics saying things were broken.
- jrockway 3y agoI agree with your take on correctness. The problem that you run into is that GC and thus finalization is triggered by memory pressure. But closing a file doesn't relieve memory pressure, it relieves FD pressure. So what you'd want is some sort of native thing not related to GC that closes unused files. This isn't the only resource like this. If you write to a lot of temporary files, you'd want to start cleaning those up before the OS returns "no space left on device". If you listen on a lot of TCP ports, you want to clean up stale listeners before you've listened 65535 times. If you span a subprocess, you want to start reaping the children when PID pressure prevents creating future subprocesses. This all ends up being rather esoteric so many programming languages just say "tell me as soon as you're done" and you pay the cost right then. This isn't optimal (especially "x := Open(thing); defer x.Close(); do something with x; do a bunch of slow stuff without x"). A big downside is that it's possible to have an object in memory that refers to something in an invalid state; you can write "x := Open(thing); x.Close(); x.Read()" just fine in many languages; the code compiles and it runs, but it returns incorrect results. This could be an area of focus for future languages, but I doubt much of this would be general. A lot of nitty gritty specifics depending on what resources you want to track. It would also be weird to not close files just because someone turned GC off, right? If you tie finalizers to memory management, if the memory management isn't under memory pressure, then you start leaking FDs. Weird! All in all, the memory management subsystem is a weird place to manage non-memory resources. So to get this right, you really need something GC-like for everything that's not memory. I think people are looking for something like, or are already very comfortable with, C++'s RAII. "I am certain this variable is allocated on the stack, so the compiler should fail to compile if that's untrue, and a Close method should be called when the function returns." But sometimes you will want the resource to be allocated beyond the current stack frame, and now you need special syntax for that. (Newer languages like to avoid exposing stack vs. heap to the programmer and let The Algorithm pick the best place. C++ never considered that an option so you do get some extra flexibility when you need it.) Anyway this is a long post to say "all options are bad".