What if close could fail?


Yesterday I made the claim that close() can’t fail in a meaningful way.  Meaning that if it’s invoked on an object in a state that does not support close(), it’s a programming/coding error.


I believe that this is correct but we’ll see that there are challenges as a result of it.


Let’s explore briefly what might happen if it could fail for environmental factors.  Are you going to write this code:


int main(int argc, const char *argv[]) {
   for (int i=1; i<argc; i++) {
      FILE *fp = fopen(argv[i], “w”);
      if (fp == NULL) {
         perror(“Unable to open file”);
         exit(EXIT_FAILURE);
      }
      // This is how it should be written but nobody does…  perror is somewhat lacking

      if (fprintf(fp, “Hello there %s\n”, argv[i]) < 0) {
         perror(“Unable to write to file”);
         fclose(fp); // should this match how fclose() is called below??  probably.
         exit(EXIT_FAILURE);
      }
      // controversial bit:
      while (fclose(fp) == EOF) {
         if (errno != ENOMEM) {
            perror(“Unable to close file”);
            exit(EXIT_FAILURE);
         }
      }
   }


   return EXIT_SUCCESS;
}


Maybe you should but I doubt it.  Maybe we should also loop on EDEADLOCK also?  ENOSPC?  All errors?  Probably not EBADF.  This error code probably means that the file handle/id in the FILE is corrupt.  Who’s going to document what?


My point here is only to try to put the nail in the coffin for resource management termination/close functions having public APIs that include failure modes other than invalid parameter.


[edited 5/3/05 2:11pm PST to fix missing return from main]
[edited 5/4/2001 4:00pm PST to fix missing close when fprintf() fails]


Comments (4)

  1. Thanks for these articles. It has been a good series. Are you going to further discuss managing multple resources?

  2. MGrier says:

    Yes, I do plan to go there (managing multiple resource). First I want to lull folks into a false sense of confidence about how it can be slightly tricky but you can hold things together. Consider in the earlier example why safe_subtract()’s return value could be ignored when using it to invert/back-out the side effects of safe_add(). One of the critical features of safe_add() and safe_subtract() are that their failure modes are well understood since we have the source code. What happens when they’re in a separate source file, header, library, dynamically loaded library or in a separate process as a server or on a remote machine? (what if safe_subtract() could fail because of a network glitch)

    Anyways, I’m going very slowly not just to walk everyone through this but because some of the results I’ve reached in my head are so suprising I want critical review as I go through the motions.

  3. In that case dont you want to close the file if fprintf fails? 😉

  4. MGrier says:

    Actually I thought about doing that and maybe I should update the example (or provide a 2nd copy which includes that). My main point was to illustrate that if an API like close() can fail, you probably can’t make forward progress until it does succeed and so you find yourself writing little loops all over the place retrying the operation.

    Unfortunately the alternative is that the implementation of close() might itself have the loop (I’m planning on this being tomorrow’s topic).

    If figuring out how to write the call to close() is really this hard, there is no hope, so I have to conclude that clients have to just call close and it has to take care of business, whatever that means.