Repository navigation
hGetContents does not close handle on exception #712
Description
Activity
There is no promise that the handle will be closed promptly, only that it will be closed eventually (namely, when the garbage collector will trigger finalisers).
But I agree that Data.Text.IO.readFile should use withFile instead of openFile. Patches are welcome.
Ah, I guess System.IO.hGetContents' also does not close the handle promptly. Is there a reason why both System.IO.hGetContents' and Data.Text.IO.hGetContents should not do so, given that they aim for non-lazy IO semantics? From what I can tell, this is what Data.ByteString.hGetContents does, but it is difficult to test since there is no easy way to provoke failure. I feel like the immediate closing behavior would be less surprising, but maybe there is a good reason for the way things are.
I guess this also means that the comment on System.IO.readFile' is not quite accurate. Using withFile is not overkill, as it ensures prompt closure of the handle which the underlying hGetContents' by itself does not guarantee.
I think I might have misled you above, I was not aware that hGetContents sets semi-closed mode.
I'm not an expert in this area: hGetContents almost immediately goes into withHandle and code there gets too dense to skim through. Perhaps the precise semantics in the presence of exceptions a good question for GHC issue tracker.
Anyway, Data.Text.IO.readFile should be modelled after System.IO.readFile' (using withFile) and not System.IO.readFile (which leaves it to hGetContents to close the handle). The current implementation is most likely a copy-paste without much analysis. Fancy to fire a PR?
We've fixed readFile but I think the behavior of hGetContents and hGetContents' here is a bug, or at the very least undesirable.
Looking at System.IO for comparison:
-
hGetContentscloses the handle on decoding exception, which is technically anIOException, so this arguably respects its spec:A semi-closed handle becomes closed:
- if
hCloseis applied to it; - if an I/O error occurs when reading an item from the handle;
- or once the entire contents of the handle has been read.
- if
-
hGetContents'does not close the handle on error. I believe this is a bug. I've opened a GHC ticket to get another opinion https://gitlab.haskell.org/ghc/ghc/-/work_items/27905
The documentation for
Data.Text.IO.hGetContentspromisesHowever, this seems to not be the case:
On my system (
ghc-9.12.2,text-2.1.4), this yieldsAs far as I can tell, this also affects
Data.Text.IO.readFile, which usesopenFileinstead ofwithFileand so the handle is not closed there either.System.IO.readFile'useswithFileand has a comment acknowledging that this should not be necessary:I cannot say with 100% certainty, but I believe this to be the root cause of a "resource exhausted (Too many open files)" exception in my application.