Thread: Wrapping sigaction() in loop until != EINTR;

  1. #1
    Registered User
    Join Date
    Mar 2008
    Posts
    44

    Wrapping sigaction() in loop until != EINTR;

    Hi,
    In another thread brewbuck pointed out that sigaction() may return EINTR and that all such calls should be wrapped in a loop to retry until they no longer return EINTR. The code being discussed was as follows:
    Code:
        sa.sa_handler = sigchld_handler; // reap all dead processes
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
            perror("sigaction");
            exit(1);
        }
    The loop I came up with looks like this:
    Code:
        sa.sa_handler = sigchld_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        while (1) {
        if (sigaction(SIGCHLD, &sa, NULL) == EINTR) {
            continue;
        } else
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
            perror("sigaction");
            exit (EXIT_FAILURE);
        }
        else break;
        }
    I do not mean to second guess brewbuck, however, after reading several sources [1][2][3] I am officially confused.
    Quote Originally Posted by [1]
    Macro: int SA_RESTART
    This flag controls what happens when a signal is delivered during certain primitives (such as open, read or write), and the signal handler returns normally. There are two alternatives: the library function can resume, or it can return failure with error code EINTR.
    The choice is controlled by the SA_RESTART flag for the particular kind of signal that was delivered. If the flag is set, returning from a handler resumes the library function. If the flag is clear, returning from a handler makes the function fail.
    Quote Originally Posted by [2]
    BSD avoids EINTR entirely and provides a more convenient approach: to restart the interrupted primitive, instead of making it fail. If you choose this approach, you need not be concerned with EINTR.
    You can choose either approach with the GNU library. If you use sigaction to establish a signal handler, you can specify how that handler should behave. If you specify the SA_RESTART flag, return from that handler will resume a primitive; otherwise, return from that handler will cause EINTR.
    Quote Originally Posted by [3]
    If a signal is caught during the system calls listed below, the call may
    be forced to terminate with the error EINTR, the call may return with a
    data transfer shorter than requested, or the call may be restarted.
    Restart of pending calls is requested by setting the SA_RESTART bit in
    sa_flags. The affected system calls include open(2), read(2), write(2),
    sendto(2), recvfrom(2), sendmsg(2) and recvmsg(2) on a communications
    channel or a slow device (such as a terminal, but not a regular file) and
    during a wait(2) or ioctl(2). However, calls that have already committed
    are not restarted, but instead return a partial success
    (for example, a
    short read count).
    While having some trouble understanding the precise meaning of the above, that last sentence of [3] makes me doubt if sa.sa_flags = SA_RESTART; is a safe enough / satisfactory solution or that a loop until != EINTR is the right thing to do.
    Will SA_RESTART properly guard over interupted system calls?

    Thanks,
    heras

    [1] http://www.delorie.com/gnu/docs/glibc/libc_485.html
    [2] http://www.delorie.com/gnu/docs/glibc/libc_498.html
    [3] http://www.hmug.org/man/2/sigaction.php

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Read the manual pages for YOUR system specifically.
    IIRC, the darker corners of signal handling are full of all sorts of implementation specific weirdness (as your various links suggest).
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    SA_RESTART is a BSD-ism that should not be relied upon, probably not even on BSD! Linux support for it is flaky at best.

    Like Salem says, understand and use the native behaviors. Wrapping syscalls in EINTR loops is required on systems which don't support syscall restarting, and harmless on systems which do. In general it is good practice.

    If you find it annoying writing all these loops all the time, then don't -- write wrapper functions for the syscalls you are using which do the loops for you.

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by heras View Post
    Code:
        while (1) {
        if (sigaction(SIGCHLD, &sa, NULL) == EINTR) {
            continue;
        } else
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
            perror("sigaction");
            exit (EXIT_FAILURE);
        }
        else break;
        }
    That loop isn't right, because sigaction() doesn't actually return EINTR -- it returns -1, and sets errno to EINTR. A better loop:

    Code:
    int ret;
    while((ret = sigaction(SIGCHLD, &sa, NULL)) < 0 && errno == EINTR)
        ;
    if(ret < 0)
    {
        /* There was an error */
    }
    Of course, a bug in this kind of code might never be found, because it is highly unlikely that sigaction() will actually fail in this way. This is another reason to wrap functions instead of coding loops everywhere -- less chance of screwing it up.

  5. #5
    Registered User
    Join Date
    Mar 2008
    Posts
    44
    I didn't realize the implementation difference, I'll stick to linux only man-pages from now on. And I'll try putting sigaction() in a wrapper function.
    Thank you both for the clarification.
    Cheers,
    heras

  6. #6
    Registered User
    Join Date
    Mar 2008
    Posts
    44
    I'm not cutting it with just the man pages and my least unsuccessful search, "sigaction example", hasn't helped me understand it much better. Some questions / assumptions:
    - SA_RESTART: "restarting a signal", though it should no be used, means that if the signal (SIGCHLD I believe) is interrupted, the signal will be sent again until it is no longer interrupted? Ie. does "restarting a signal" mean "sending it again"?
    - Omitting to set sa_flags causes the program to segfault right after the child exits. Is this because the default is then SA_SIGINFO which expects 3 arguments while my handler only accepts one? Should I use this and change my handler?
    - In an example on the web I found the construct below to set the sa_flags and the source says it's a standard of some kind but does not explain what it does and why you'd want to. For example: "Restore the signal action to the default state once the signal handler has been called.", I don't know how to interpret that.
    - If I would use SA_NOCLDWAIT, does that mean that the parent will not know whether or not the child was successfully reaped and thus a bad idea?
    - Am I entirely missing the point here and should I be looking for something else?

    Thanks,
    heras

    edit: btw, the code below fails to clean up child processes and intermittently returns "accept: Interrupted system call".

    Code:
    void sigchld_handler(int s)
    {
    	while (waitpid(-1, NULL, WNOHANG) > 0);
    }
    Code:
    int check_sigaction(struct sigaction *sa)
    {
    	int ret;
    	while ((ret = sigaction(SIGCHLD, sa, NULL)) == -1 && errno == EINTR);
    	return (ret);
    }
    Code:
    	sa.sa_handler = sigchld_handler;
    	sigemptyset(&sa.sa_mask);
    	sa.sa_flags = 0;
    	sa.sa_flags = sa.sa_flags | SA_NODEFER | SA_RESETHAND;
    	if (check_sigaction(&sa) == -1) {
    		perror("sigaction");
    		exit (EXIT_FAILURE);
    	}
    
    	while (1) {
    		sin_size = sizeof remote_address;
    		if ((new_fd = accept(sockfd, (struct sockaddr *)&remote_address, \
    				&sin_size)) == -1) {
    			perror("accept");
    			continue;
    		}
    		if (!fork()) {
    			close(sockfd);
    			if (recv(new_fd, message, sizeof(message), 0) == -1) {
    				perror("recv");
    			}
    			else {
    				printf("&#37;s\n", message);
    				send(new_fd, reply, strlen(reply) + 1, 0);
    			}
    			close(new_fd);
    			exit (EXIT_SUCCESS);
    		}
    		close(new_fd);
    	}
    	return (EXIT_SUCCESS);
    }
    Last edited by heras; 04-05-2008 at 06:28 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 07-07-2009, 10:05 AM
  2. My loop within loop won't work
    By Ayreon in forum C Programming
    Replies: 3
    Last Post: 03-18-2009, 10:44 AM
  3. nested loop, simple but i'm missing it
    By big_brother in forum C Programming
    Replies: 19
    Last Post: 10-23-2006, 10:21 PM
  4. while loop help
    By bliznags in forum C Programming
    Replies: 5
    Last Post: 03-20-2005, 12:30 AM
  5. loop issues
    By kristy in forum C Programming
    Replies: 3
    Last Post: 03-05-2005, 09:14 AM