Search code examples
clinuxpthreadssignalsblocking

blocking function accept() restarts when SIGINT occurs even though SA_RESTART flag is set to zero


I'm writing a C program on Linux, where I have a main() and two pthreads created by it. In one of the pthreads I have a call to accept() function.

I have a signal handler which is invoked upon receiving of SIGINT, SIGQUIT or SIGTERM.

My expectation is, because I am making SA_RESTART flag zero, when I press ctrl-c, accept() function should return EINTR instead of getting restarted, however I realised via bunch of printf calls while debugging (to see which lines are executed by printing where the code is), that even though my application is able to catch the SIGINT, the accept function remains blocked, it is not failing with EINTR and not moving to next line of code. Here are my settings inside main()

struct sigaction signal_action;
signal_action.sa_flags = 0; // Don't restart the blocking call after it failed with EINTR
signal_action.sa_handler = terminate;
sigemptyset(&signal_action.sa_mask);
sigfillset(&signal_action.sa_mask); // Block every signal during the handler is executing
if (sigaction(SIGINT, &signal_action, NULL) < 0) {
    perror("error handling SIGINT");
}
if (sigaction(SIGTERM, &signal_action, NULL) < 0) {
    perror("error handling SIGTERM");
}
if (sigaction(SIGQUIT, &signal_action, NULL) < 0) {
    perror("error handling SIGQUIT");
}

Here is the signal handler:

void terminate (int signum)
{
    terminate_program = 1;
    printf("Terminating.\n");
}

Here is the pthread where the accept() call is made (I tried to delete unrelated stuff to make my question more understandable):

void* pthread_timerless_socket_tasks(void* parameter)
{

    server_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(server_socket_fd < 0)
    {
        perror("error creating IPv4 TCP stream socket");
        return (NULL);
    }
    printf("socket created\n"); // for debugging
    if(fcntl(server_socket_fd, F_SETFL, 0) < 0)
    {
        perror("error making socket_fd blocking");
        close(server_socket_fd);
        return (NULL);
    }

    while(!terminate_program)
    {
        printf("socket blocking on accept\n"); // for debugging
        client_socket_fd = accept(server_socket_fd,(struct sockaddr *) &client_address, &client_length);
        printf("socket accepted?\n"); // for debugging
        if(client_socket_fd < 0)
        {
            perror("error accepting socket_fd");
            close(server_socket_fd);
            return (NULL);
        }


    } 

I hope I made myself clear.

So, now I am wondering, what is missing or incorrect as I am unable to see the behaviour described in the linux manuals for SA_RESTART.


Solution

  • From the signal(7) manual page:

    If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

    What that means is that if the signal is not sent to the thread doing the accept, then the call won't be interrupted by the signal.

    You should block (by setting the signal mask) the signals in all other threads, then the only thread that can receive the signal is the one calling accept.