gcbenison
gcbenison

Reputation: 11963

When is it better to explicitly close file handles?

My first impression of readFile was of a tradeoff between its convenience and the possibility of it leaving file descriptors open longer than needed, with no way to close them. As an experiment I tried the following (highly practical) program, thinking it might choke by trying to maintain a thousand open file descriptors:

main = do
  mapM_ (\idx -> readIt) [1..1000]
  where readIt = do
          contents <- readFile "/etc/passwd"
          putChar $ head contents

But it actually does a pretty good job of reclaiming file descriptors; the count never gets above around 70:

open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 4
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 5
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 6
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 7
...
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 65
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 66
close(3)                                = 0
close(4)                                = 0
close(5)                                = 0
...
close(54)                               = 0
close(55)                               = 0
close(56)                               = 0
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 3
open("/etc/passwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 4

How is this happening? Is it just that the values of contents are getting GC'd, and with them the no-longer-referenced file descriptors? Or is there some separate mechanism for managing file descriptor resources? Whatever the mechanism is, it seems to be working pretty well - how can you know when it's best to use hClose explicitly?

Upvotes: 8

Views: 517

Answers (2)

Don Stewart
Don Stewart

Reputation: 137987

It is better to explicitly close resources yourself, only when you have some low level resource constraint that you can actually enforce manually.

The cases to consider:

  • resources aquired via lazy IO: must use the GC to free the resource
  • strict IO: can close manually once input is read; or use a bracketing combinator (e.g. finally or bracket)
  • incremental IO (conduits, iteratees): let the framework close it for you.

Upvotes: 8

Nathan Kleyn
Nathan Kleyn

Reputation: 5143

The Haddock docs for System.IO have this to say:

GHC note: a Handle will be automatically closed when the garbage collector detects that it has become unreferenced by the program. However, relying on this behaviour is not generally recommended: the garbage collector is unpredictable. If possible, use an explicit hClose to close Handles when they are no longer required. GHC does not currently attempt to free up file descriptors when they have run out, it is your responsibility to ensure that this doesn't happen.

Upvotes: 6

Related Questions