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.