2

Possible Duplicate:
Wake up thread blocked on accept() call

I am writing a small server which listening for connections (accepting them and passing them to worker threads) until a custom stop signal is sent.

If I use blocking sockets, then my main accept loop cannot break on the custom stop signal being sent. However, I wish to avoid having a busy-wait/spinlock loop with a non-blocking socket.

What I want is for my main accept loop to block until either a connection is received or the stop signal is sent.

Is this possible in C on Linux?

Many Thanks.

Community
  • 1
  • 1
Technosites
  • 67
  • 1
  • 6
  • 1
    this shouldn't happen, the signal should be able to interrupt accept unless you're blocking that signal – iabdalkader Nov 10 '12 at 15:25
  • 1
    Are you sure that SIGSTOP doesn't break an `accept()` call? POSIX says 'The system shall not allow the action for the signals SIGKILL or SIGSTOP to be set to SIG_IGN.' It also says the 'default' action is: '_SIGSTOP — Stop the process — Stop executing (cannot be caught or ignored)._' That is, a process that is SIGSTOP'd cannot stop itself being SIGSTOP'd. – Jonathan Leffler Nov 10 '12 at 15:26
  • @JonathanLeffler so it can't even be blocked ? – iabdalkader Nov 10 '12 at 15:29
  • 1
    Did you mean to say SIGTERM or SIGINT? Because SIGSTOP really just “pauses” the execution of your program. It'll not notice and it will resume on SIGCONT. These two signals will stop the accept() call, which should return with an EINTR error. – Jonas Schäfer Nov 10 '12 at 15:33
  • Sorry for the ambiguity, I don't actually mean SIGTERM or SIGINT or even SIGSTOP! I mean a custom signal - basically when one of the worker threads receives a command over a socket I want the main server thread to shutdown and stop listening – Technosites Nov 10 '12 at 15:36
  • 1
    @mux Yes. As stated by `man sigprocmask(2)`: "*It is not possible to block SIGKILL or SIGSTOP. Attempts to do so are silently ignored.*" – alk Nov 10 '12 at 15:39
  • So what do you want to use for the signal? A POSIX signal? another socket? a pipe? a file that gets created? a semaphore? – Celada Nov 10 '12 at 15:40
  • 1
    SIGSTOP cannot be blocked. You can control what happens with most signals, but you cannot block SIGSTOP or SIGKILL. What is a 'custom signal'? You can arrange for a signal handler to be called on most signals. SIGUSR1 or SIGUSR2 might be appropriate. System calls are usually interrruptible; some of them are resumable and others are not. (Some system calls — like `getpid()`, I believe — are not formally interruptible; the interrupt is delivered to the process as it transitions back to user-land. The difference is minimal at the code level.) – Jonathan Leffler Nov 10 '12 at 15:41
  • This comes down to "Did you try it?", where in this context, the 'it' is 'writing a loop with an `accept()` call and then sending it a signal'? If so, show the code; if not, try it. – Jonathan Leffler Nov 10 '12 at 15:44
  • @Celada the simplest signal possible ideally :P a global flag variable / semaphore or a pthreads signal perhaps? I'm using the term 'signal' in a very informal sense here, I apologise! Currently I'm just using a global int as a flag, so I'm not sure how I can 'try it' and get it to interrupt the accept... – Technosites Nov 10 '12 at 15:44

2 Answers2

2

If I understand correctly then you are open to using any kind of "signal", not necessarily a POSIX signal. Actually, POSIX signals are a poor choice because checking if you have received one in a loop has unavoidable race conditions.

What you need to use is anything that can be monitored through a file descriptor. It could be:

  • A pipe. Your accept loop monitors the read end of the pipe. To wake up the loop, another thread writes something into (doesn't matter what) into the write end.
  • Another socket. Similarily, another thread wakes up your accept loop by writing into the other end of the socket.
  • A file in the filesystem which you monitory using inotify.
  • A device which receives some data when the loop should be interrupted.
  • etc...

The later entries in the list of examples aren't generally practical. They're just to illustrate that it can be any type of object as long as it is has monitorable file descriptor. The simplest, cheapest, and most popular way is a pipe.

If you are already using nonblocking sockets, then you certainly already have some kind of polling loop to check when they're ready to accept connections. I'm going to assume you're using poll() to do this.

Before you start your loop, set up a pipe like this:

pipe(&pipefd[0]);
read_end = pipefd[0];
write_end = pipefd[1];
fcntl(read_end, F_SETFL, O_NONBLOCK);

The fcntl is to set the read end of the pipe to non blocking mode. You're going to be using that end of the pipe in your poll call together with your socket, so it needs to be nonblocking.

Then just add the read end of the pipe to the list of tile descriptors that you monitor in your accept loop:

for (;;) { /* accept loop, loop forever */
    /* Monitor your socket. You should already have this in your code */
    pollfd[0].fd = your_socket;
    pollfd[1].events = POLLIN;

    /* Add a new entry to monitor the read end of the pipe */
    pollfd[1].fd = read_end;
    pollfd[1].events = POLLIN;

    /* Now call poll, as before, but monitor 2 file descriptors instead of just 1 */
    n = poll(&pollfd[0], 2, -1);

    /* Check if your socket is ready to accept.
       This part, you are already doing. */
    if (pollfd[0].revents) {
        newsocket = accept(your_socket, etc....)
        do_somehting_with(new_socket);
    }

    /* New part: check if anyone has written something for you into your pipe */
    if (pollfd[1].revents) {
        break; /* stop the loop */
    }
}

The example uses poll but you might be using select (or even epoll) instead. select has a different interface but the idea is the same.

This technique is known as the self-pipe trick.

Celada
  • 21,627
  • 4
  • 64
  • 78
  • Thank you for a very informative reply. I'm not currently using `poll` or `select` but now realise I should be, and the self-pipe trick seems to be exactly what I need! – Technosites Nov 10 '12 at 22:22
0

If you do not to want pass an unblocking socket to accpet(), then sending a signal (as described by man signal to the accept()ing thread is the only way to interrupt a blocking accept() (waiting for connections).

As already pointed out by some commenters most system calls (including accept()) will be interupted when their thread received a signal.

If accept() returns -1and errno is set to EINTR the call to accept() was interupted by a signal.

To send a signal to a process use kill(<pid>, <signal-number>).

If you are in a multi-threaded enviroment, block the signal you'll be using (typical SIGUSR1 or SIGUSR2) for all other threads, but the thread that calls accept(), asi t is nondeterministic which thread of a process will handle a signal sent to a process ("addressed" via the process' pid.)

Alternativly you can use pthread_kill(<pthread>, <signal-number>) to send a signal to a specific thread only.

alk
  • 69,737
  • 10
  • 105
  • 255
  • Thanks for the reply, its very helpful in understanding how signals work, though I'm probably going to go with the self-pipe trick instead for simplicity. – Technosites Nov 10 '12 at 22:23