The so-called I/O primitives of C include the commonly used read, write, and open,. Even though alternatives exist (like fwrite and its siblings, provided by the standard C library) these still see a lot of use. Its well known that these all return a value that indicates if the operation succeeded or failed, and that errno is also set, allowing the precise failure case to be determined. Most code checks these return values and handles the less than zero case by issuing some error message based on the errno. Less common is the proper handling of the EINTR errno. Most programmers seem to know that you should handle EINTR on reads and writes:


     rc = read(fd, buffer, len)
     if (rc < 0) {
        if (errno != EINTR)) {
            perror("read error:")
            return (-1);
         } else {
            // some retry handling
         }
     }
 

(Its also common to see the above in a while or do-while loop.) What is less common is to see the proper handling of EINTR on the other primitives. I rarely see it handled on open operations, though its definitely a possible case that should be handled (see the man page for open for more info). This man page on signals lists all the system calls that can be interrupted by signal handlers, and thus require the special handling. Its worth checking out (its towards the bottom of the page) since the list is quite extensive. It's also worth mentioning that close is NOT on this list. If you take a look at the system call implementation in the Linux kernel you'll notice that regardless of the error returned, the file descriptor is closed. It seems that even an EINTR errno from close does not really mean that it was interrupted and should be retried. Take a look at the source code.


  SYSCALL_DEFINE1(close, unsigned int, fd)
  {
  	int retval = __close_fd(current->files, fd);

  	/* can't restart close syscall because file table entry was cleared */
  	if (unlikely(retval == -ERESTARTSYS ||
  		     retval == -ERESTARTNOINTR ||
  		     retval == -ERESTARTNOHAND ||
  		     retval == -ERESTART_RESTARTBLOCK))
  		retval = -EINTR;

  	return retval;
  }
  
  


Take note of the comment in the code! Even though it returns EINTR, the entry in the descriptor table has already been cleared! Retrying would be a bad idea on a loaded machine; the fd could have been re-used while you are retrying the close.

As an interesting aside, if you are using gcc as your compiler, you can use the TEMP_FAILURE_RETRY macro to perform the retry in a much cleaner and easier to read fashion.


bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, len));