libstddjb
libskarnet
skalibs
Software
skarnet.org

Safe wrappers

Lots of functions in libstddjb, declared for instance in allreadwrite.h or djbunix.h, are just "safe wrappers" around corresponding system functions. For instance, fd_read() is a safe wrapper around the system read() function.

The problem

Quite a lot of system calls are defined by The Open Group Base Specifications as interruptible: when the process is in the middle of such a system call and receives a signal that it does not ignore, the system call immediately returns -1 EINTR (after the signal handler, if any, has been executed).

Most of the time, this is not an issue: unignored signals usually kill the process anyway. Or stop it - and when it resumes, system calls are simply restarted, not interrupted. EINTR can only happen when a signal handler has been installed, and a signal is actually caught by the signal handler during an interruptible system call. And to avoid EINTR, users can use the SA_RESTART flag when installing the signal handler with sigaction().

However, there is a common case where using SA_RESTART is a bad idea: when writing an asynchronous event loop.

An asynchronous event loop is organized around a select() or poll() system call that is the only blocking operation in the whole loop. That call takes a timeout as an argument. If a signal handler is installed with SA_RESTART and a signal arrives in the middle of the select/poll call - which happens often since it is blocking - then the select/poll is restarted with the same arguments, including the timeout. This is not good: time has elapsed, the timeout should be recomputed. Some versions of select update the values in the struct timeval even when select() is interrupted, but it is not portable, and not applicable to poll(). So it is necessary, in this case, to have select/poll return -1 EINTR when a signal is caught. And that means SA_RESTART should not be used.

Which also means that other system calls performed when the signal handler has been installed, for instance in the body of the loop, will not be protected, and can return -1 EINTR if a signal is caught at the wrong time.

The solution

So, in order to be perfectly reliable, when a program has caught a signal without SA_RESTART and makes an interruptible system call, it must check whether the return value is -1 EINTR, and restart the system call if it is the case. This is annoying to write; so, skalibs provides small wrappers around interruptible system calls, so that programmers can just call those safe wrappers and never bother with this again.

The performance loss from having a wrapper layer is totally negligible compared to the cost of using a system call in the first place.