1

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.

3
  • This if(fcntl(server_socket_fd, F_SETFL, 0) ... looks strange, to not say wrong. Commented Apr 15, 2016 at 11:15
  • @alk I was trying to make the file descriptor blocking this way, as by default it wasn't. I'm open to suggestions:) Commented Apr 15, 2016 at 13:07
  • On how to toggle to the blocking-state of a socket using fcntl() you might like to have looks this answer: stackoverflow.com/a/1549344/694576 Commented Apr 15, 2016 at 15:22

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

12 Comments

I moved the "settings" code (where I call the sigaction and set sa_flags to zero) from the main() to the thread with accept() (and made sure no other thread has this code), to make sure the signal is delivered to the right thread but still accept() is blocking.
I realised from your post, I need to block other threads too, I'll try it now.
@etugcey Open the linked manual page, and search for "Signal mask and pending signals". That will tell you which functions you need to block and unblock signals per process and per thread.
@etugcey welcome to pthreads. Stopping threads with blocking system calls cleanly is one of the hardest problems there is. Your next idea will be to look at pthread_cancel. Ignore that. It will cause you even more trouble. The best way I've found to do this is to not block on anything other than poll and have each thread poll on a file descriptor (eventfd on linux, pipe on every other system) while also polling on the other file descriptor (like your listen socket) and use that to signal the threads that they need to exit (or do something else).
@art ... or install a single (process-wide) thread catching all signals and let the latter dispatch the signal received as needed using pthread_kill().
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.