POSIX/UNIX: How to reliably close a file descriptor

There's no practical solution for this problem as POSIX doesn't address this at all.

Not retrying the close may result in unusable open file descriptors piling up.

As much as it sounds like legitimate concern, I have never seen this happen due to failed close() calls.

A clean solution might involve invoking fstat() on the freshly closed file descriptor and a quite complex locking mechanism.

Not really. When close() failed, the state of the file descriptor is unspecified. So, you can't reliably use it a fstat() call. Because the file descriptor might have been closed already. In that case, you are passing an invalid file descriptor to fstat(). Or another thread might have reused it. In that case, you are passing the wrong file descriptor to fstat(). Or the file descriptor might have been corrupted by the failed close() call.

When process exits, all the open descriptors will be flushed and closed anyway. So, this isn't much of a practical concern. One could argue that this would be a problem in a long running process in which close() fails too often. But I have seen this happen in my experience and POSIX doesn't provide any alternative either.

Basically, you can't do much about this except report that the problem occurred.


To mitigate any issues, explicitly sync the file:

  1. (If you are operating on FILE*, first call fflush() on it to make sure user space buffers are emptied to kernel.)
  2. Call fsync() on the file descriptor, to flush any kernel data and metadata about the file to disk.

These you can retry on error without extra worries. After that, possibly leaking file descriptors or handles on interrupted close on some OSes is probably a minor issue, especially if you check the behavior for OSes which are important to you (I suspect there's no problem in most relevant OSes).

Also, once the file and data are flushed, the chances of getting interrupted during close is much smaller, as then close should not actually touch disk. If you do get EIO or EINTR anyway, just (optionally) log it and ignore it, because doing anything else probably does more harm than good. It's not a perfect world.


This issue has been fixed in POSIX for the next issue; unfortunately it's too big a change to have made it into the recent TC2. See the final accepted text for Austin Group Issue #529.