System calls and EINTR error code
Asked Answered
S

4

7

Is there any expert out there that can help me with the following?

I have the following system calls in C:

access()
unlink()
setsockopt()
fcntl()
setsid()
socket()
bind()
listen()

I want to know if they may fail with error code -1 and errno EINTR/EAGAIN.

Should I have to handle EINTR/EAGAIN for these?

The documentation do not refer anything related to EINTR/EAGAIN but many people I see handle it.

Which is the correct?

Here is how I register signal handlers : https://gitorious.org/zepto-web-server/zepto-web-server/source/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd:src/signal-dispatcher-utility.c

With this configuration: https://gitorious.org/zepto-web-server/zepto-web-server/source/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd:src/server-signals-support-utility.c

Also here is a commit that I added some EINTR/EAGAIN handling in some system calls that I know that return EINTR or EAGAIN : https://gitorious.org/zepto-web-server/zepto-web-server/commit/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd

Swadeshi answered 8/9, 2014 at 17:33 Comment(6)
Depending on the platform, on the system call and the SA_RESTART flag, the system call may or may not fail with EINTR. (read in pep-0475)Choroid
possible duplicate of Which system calls can return EINTR or EAGAIN error codes?Chicory
I have enabled SA_RESTART. However, I have read that it should be nice to handle also EINTR explicitly.Swadeshi
Any other ideas? Should I handle EINTR for the calls or the SA_RESTART is enough?Swadeshi
EINTR and EAGAIN mean different things. I would recommend always writing code to handle EINTR. If you get EAGAIN when you didn't ask for it, there's a serious logic error either in your program or in the kernel.Dull
EAGAIN and EINTR are most of the times for repeating the call like in fork(), read(), write() where I want to re-execute the call. Either because of a EWOULDBLOCK==EAGAIN reason or because of interruption. So you believe that it is a good practice to always handle the EINTR regardless of the system call?Swadeshi
M
4

See http://man7.org/linux/man-pages/man7/signal.7.html -- start reading near the bottom where it talks about "Interruption of system calls and library functions..." This is a Linux man page, but the info is pretty generally applicable to any Unix/Posix/Linux-flavored system.

Muscular answered 8/9, 2014 at 17:38 Comment(8)
I have read it, for the reffered system calls I have handled the EINTR. But, only these are the system calls that return EINTR? I do not think so. Many people handle EINTR in other system calls that their man page do not refers it. Why do the do it? Is it be good practice or is it something like magic?Swadeshi
Any other ideas? Should I handle EINTR for the calls or the SA_RESTART is enough?Swadeshi
Personally, I generally opt for checking for EINTR over setting SA_RESTART because in general I want to know when something happened. If you see a check for EINTR on something which doesn't return it, then I would say that's either superstition or "something"s documentation is wrong (and it does return EINTR).Muscular
Yes, but what should I do now for the referred system calls that I know they don't return EINTR but people handle EINTR. Is it wrong because of documentation or superstition? I don't want to start watching the source code of the system calls in the Linux kernel...Swadeshi
As a rule only syscalls which are "slow" return EINTR. Slow things are terminal I/O and things which wait (select, wait, sleep, pause, etc). If you see somebody checking EINTR on, say, seteuid() then the programmer is nuts.Muscular
Great man, that was my answer! So, I have to check which system calls are time-consuming. Thank you very much!Swadeshi
One more question: I can handle EINTR to any system call? I have to see if the system call returns it? In your opinion is there any system call from the referred that you could handle it?Swadeshi
You can always do if (somesyscall() == -1) { if (errno == EINTR) {...} } (as long as a return of -1 indicates an error). The only "penalty" is a few wasted cycles and future viewers of your code making fun of you if somesyscall() never fails with EINTR.Muscular
B
6

Unless you install an interrupting signal handler (one installed with sigaction omitting the SA_RESTART flag, or one installed with the signal function on some systems) you should not expect to see EINTR at all.

Among your particular list of functions, I don't see any that could experience EINTR anyway except fcntl, and for it, only when it's used for locking. The link in John's answer should be helpful answering questions about specific functions, though.

Bowler answered 8/9, 2014 at 17:41 Comment(2)
I have installed many signal handlers with sigaction and most of these use SA_RESTART. Also the signal handlers cannot be interrupted. In some articles and threads I have read that we should also check for EINTR regardless of the SA_RESTART. I'm using HANDLE_EINTR and HANDLE_EAGAIN macros (grabbed from Chrome source code).Swadeshi
Any other ideas? Should I handle EINTR for the calls or the SA_RESTART is enough?Swadeshi
M
4

See http://man7.org/linux/man-pages/man7/signal.7.html -- start reading near the bottom where it talks about "Interruption of system calls and library functions..." This is a Linux man page, but the info is pretty generally applicable to any Unix/Posix/Linux-flavored system.

Muscular answered 8/9, 2014 at 17:38 Comment(8)
I have read it, for the reffered system calls I have handled the EINTR. But, only these are the system calls that return EINTR? I do not think so. Many people handle EINTR in other system calls that their man page do not refers it. Why do the do it? Is it be good practice or is it something like magic?Swadeshi
Any other ideas? Should I handle EINTR for the calls or the SA_RESTART is enough?Swadeshi
Personally, I generally opt for checking for EINTR over setting SA_RESTART because in general I want to know when something happened. If you see a check for EINTR on something which doesn't return it, then I would say that's either superstition or "something"s documentation is wrong (and it does return EINTR).Muscular
Yes, but what should I do now for the referred system calls that I know they don't return EINTR but people handle EINTR. Is it wrong because of documentation or superstition? I don't want to start watching the source code of the system calls in the Linux kernel...Swadeshi
As a rule only syscalls which are "slow" return EINTR. Slow things are terminal I/O and things which wait (select, wait, sleep, pause, etc). If you see somebody checking EINTR on, say, seteuid() then the programmer is nuts.Muscular
Great man, that was my answer! So, I have to check which system calls are time-consuming. Thank you very much!Swadeshi
One more question: I can handle EINTR to any system call? I have to see if the system call returns it? In your opinion is there any system call from the referred that you could handle it?Swadeshi
You can always do if (somesyscall() == -1) { if (errno == EINTR) {...} } (as long as a return of -1 indicates an error). The only "penalty" is a few wasted cycles and future viewers of your code making fun of you if somesyscall() never fails with EINTR.Muscular
P
2

There is a section entitled ERRORS in every man page of *NIX system call. Refer to the manual, for example : http://man7.org/linux/man-pages/man2/accept.2.html. You can also use the command line man accept to view it.

In general, system calls that can take some time to compute can set -1+EINTR on signal delivery and short system calls not. For example, accept() can block your process so it can be interrupted by a signal, but setsid() is so short that it has been written to not be interrupted by signals.

Promptbook answered 8/9, 2014 at 18:13 Comment(14)
The first thing I did was to take information from man pages and Open Group specification. All the referred system calls do not refer to EINTR or EAGAIN in their manual page. But I think that some of these or even all may return EINTR because they may use inner modules or calls that may fail with EINTR and back propagate that error. Many system calls refer to such things. For example fopen refers to man page that errno may returned by open which is used inside it... Still searching.Swadeshi
Also, the system call 'close' although it refers the EINTR in case of error in man page it is wrong to handle it. Usually we ignore it. This is said from Google people in Android and Chrome projects.Swadeshi
@EfstathiosChatzikyriakidis: If that's permitted by the standard, it will say it explicitly, as in your example of fopen. Note that some functions are documented explicitly not to be able to fail with EINTR though (mainly, most of the POSIX threads functions) so perhaps you can read that as implying that EINTR can happen in other functions that aren't explicitly documented not to be affected by it.Bowler
@EfstathiosChatzikyriakidis: Regarding close, that advice you found is only "correct" as a workaround for a Linux bug. See ewontfix.com/4 (disclosure: my blog).Bowler
So, you believe that I would not handle the EINTR from the referred system calls since the man page do not refer anything at all? Only the socket system call refers something like: "Other errors may be generated by the underlying protocol modules.". Also, fcntl return EINTR in case of locking but I dont care for it.Swadeshi
fopen is not a system call, it is a library function call. It calls open behind the scene...Evermore
If you're actually using interrupting signals in your program, it may be wise to "play it safe" and check for EINTR even in places where you "shouldn't need to". But why are you using interrupting signal handlers if you're just going to restart the operation whenever you see EINTR? The whole point of interrupting signal handlers is to allow yourself to break out of blocking operations when a signal happens (but doing this has fundamental, unsolvable race conditions)...Bowler
So, regarding close is just a Linux kernel issue right?Swadeshi
You've made a mistake as the Open Groupe specs did specify if a call can be interrupted or not. See for accept pubs.opengroup.org/onlinepubs/009695399/functions/accept.htmlEvermore
close is a system call and can be interrupted.Evermore
@Jean-Baptiste Yunès: Sorry I meant access not accept.. I have handled the access system call.Swadeshi
@Jean-Baptiste Yunès: Close is system call and can be interrupted but in Linux kernel retrying to close the file descriptor again may lead to an error. See: review.webrtc.org/4759004Swadeshi
@R..: I'm using signal handlers because is a server and when child processes are exited they send to the server a SIGTERM. Also when I kill the server I send the SIGTERM signal.Swadeshi
@EfstathiosChatzikyriakidis: I don't see any reason for those to be interrupting though.Bowler
P
0

signal(7) for Linux lists

accept
connect
fcntl
flock
futex
ioctl
open
read
readv
recv
recvfrom
recvmsg
send
sendmsg
sendto
wait
wait3
wait4
waitid
waitpid
write
writev

as possibly interruptible (EINTR) by no-SA_RESTART handlers and

setsockopt
accept
recv
recvfrom
recvmsg
connect
send
sendto
sendmsg
pause
sigsuspend
sigtimedwait
sigwaitinfo
epoll_wait
epoll_pwait
poll
ppoll
select
lect
msgrcv
msgsnd
semop
semtimedop
clock_nanosleep
nanosleep
read
io_getevents
sleep

as EINTR-interruptible, even by SA_RESTART handlers.

Furthermore, it lists:

setsockopt
accept
recv
recvfrom
recvmsg
connect
send
sendto
sendmsg
epoll_wait
epoll_pwait
semop
semtimedop
sigtimedwait
sigwaitinfo
read
futex
msgrcv
msgsnd
nanosleep

as EINTR-interruptible by a stopping signal + SIGCONT, and says this particular behavior is Linux-specific and not sanctioned by POSIX.1.

Apart from these, especially if the function's specification doesn't list EINTR, you shouldn't get EINTR.

If you don't trust the system to honor it, you can try bombarding a loop with your suspected system function by SIGSTOP/SIGCONT+a signal with a no-SA_RESTART no-op handler and see if you can elicit an EINTR.

I tried that with:

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static void chld(int Sig)
{
    int status;
    if(0>wait(&status))
        _exit(1);
    if(!WIFEXITED(status)){ 
        //this can only interrupt an AS-safe block
        assert(WIFSIGNALED(status) && WTERMSIG(status) == SIGALRM);
        puts("OK");
        exit(0);
    } else {
        switch(WEXITSTATUS(status)){
        case 1: puts("FAIL"); break;
        case 2: puts("EINTR"); break;
        }
    }
    exit(0);
}
static void nop(int Sig)
{
}

int main()
{
    sigset_t full;
    sigfillset(&full);
    sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=chld, .sa_mask=full, .sa_flags=0 } , 0); 
    sigaction(SIGUSR1, &(struct sigaction){ .sa_handler=nop, .sa_mask=full, .sa_flags=0 } , 0); 

    pid_t p;
    if(0>(p=fork())) { perror(0); return 1; }
    if(p!=0){
        //bombard it with SIGSTOP/SIGCONT/SIGUSR1
        for(;;){
            usleep(1); kill(p, SIGSTOP); kill(p, SIGCONT); kill(p, SIGUSR1);
        }
    }else{
        sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=SIG_DFL }, 0);
        if(0>alarm(1))
            return 1;
        for(;;){

    #if 1
            /*not interruptible*/
            if(0>access("/dev/null", R_OK)){

                if(errno==EINTR)
                    return 2;
                perror(0);
                return 1;
            }

    #else
            int fd;
            unlink("fifo");
            if(0>mkfifo("fifo",0600))
                return 1;

            /*interruptible*/
            if(0>(fd=open("fifo", O_RDONLY|O_CREAT, 0600))){
                if(errno==EINTR)
                    return 2;
                perror(0);
                return 1;
            }
            close(fd);
    #endif

        }
    }
    return 0;
}

and unlink and access definitely appear to be EINTR-uninterruptible (in compliance with their spec), which means an EINTR-retry loop around them would be unnecessary.

Pennsylvanian answered 8/11, 2017 at 13:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.