Tuesday, August 30, 2011

Handling Interrupts

interrupted System Calls issue:

Reading the Linux man page for fread(3) or fwrite(3) doesn't reveal much about the possible errors that can be returned. Only the return value is described, which indicates that if the returned count is short, or the count is zero, an error has occurred.

The fread(3) and fwrite(3) functions are described in more detail within the AT&T System V Interface Definition documentation. What is interesting about this UNIX documentation is the fact that the error code EINTR can be returned.

The AT&T System V Interface Definition (SVID) was one attempt to specify a UNIX operating system environment that allowed applications to be created, which was independent of the computer hardware used. The SVID standard was stated as compliant with the POSIX 1003.1 Full Use Standard and the ANSI C X3J11 industry standard.

The EINTR error indicates that an interrupted system call has occurred. This error is returned when your process has been signaled, and a signal handler has been called to process it, and that handler has returned from its call. This error code is not returned by all function calls, but it is returned in instances in which the function call might block for a long period of time. Certainly, a read(2) call waiting for incoming data on a socket fits in this category.

You'll recall that the fread(3) function call is simply a functional layer over the underlying read(2) function call that is invoked as required by buffering. Consequently, it follows that the fread(3) might be susceptible to the EINTR error code, if signals have been received by your process. The same is also true of the fwrite(3) function call. If a large volume of data is written to a socket, the underlying write(2) call might also block for a long time. If, while it is blocked, a signal is received and handled, the write(2) function will return an EINTR error, which might cause the fwrite3) function to return this error.

The word "might" was used because this depends upon the design of the stdio(3) library that you are using. I have seen some UNIX implementations hide this error from the caller, whereas others return EINTR. Given that Linux has generally been moving from the libc5 library to the newer glibc2 version of the C libraries, your mileage might vary.

Some simple testing for this under Red Hat Linux 6.0 suggests that EINTR will not be returned. However, as the GNU C library code moves with standards, which themselves are undergoing revision and further clarification, this might change. If your application must run on other UNIX platforms in addition to Linux, then you should test for EINTR in your code.

If you must allow for EINTR in your code, then the following code fragment represents a template that you might use:


int ch;
do {
   ch = fgetc(rx);
} while ( ferror(rx) && errno == EINTR );

The basic procedure used here is
  1. Call clearerr(3) to clear any pending error that might have occurred on this stream.
  2. Perform your input/output operation.
  3. If the operation failed and the errno value was set to EINTR, then repeat step

After the code has exited the loop, this indicates that the operation either succeeded, or it failed with a different error code other than EINTR. The general principle at work here is that you retry the operation when the error EINTR is returned.

Simple tests that were performed by the author suggested that the Red Hat Linux 6.0 distribution included a C library that hides the EINTR error from the application code. This can be both a blessing and a curse to the programmer. It is a blessing in the sense that you do not have to code to handle the error condition. It is a curse if you need to test to see whether a signal was processed by the signal handler while execution was blocked in the function call.

Handling EINTR for Other Functions

It should be noted at this point that EINTR is potentially a problem for a host of other functions that you might use for socket programming. The functions affected by signals include

• connect(2)
• accept(2)
• read(2)
• write(2)
• readv(2)
• writev(2)
• recvfrom(2)
• sendto(2)
• select(2)
• poll(2)

The list presented is not meant to be an exhaustive list, and some of these functions have not been covered yet. It is a list of commonly used functions, however, which are affected by signal handling. The examples that are shown in this text will largely ignore this issue of EINTR, in favor of keeping the example programs small and easier to understand. However, you must allow for the occurrence of EINTR for any production-level code.

Always test for the EINTR error when calling functions affected by signal handling in production-level code.

No comments:

Post a Comment