Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I "disengage" from `accept` on a blocking socket when signalled from another thread?

I am in the same situation as this guy, but I don't quite understand the answer.

The problem:

  • Thread 1 calls accept on a socket, which is blocking.
  • Thread 2 calls close on this socket.
  • Thread 1 continues blocking. I want it to return from accept.

The solution:

what you should do is send a signal to the thread which is blocked in accept. This will give it EINTR and it can cleanly disengage - and then close the socket. Don't close it from a thread other than the one using it.

I don't get what to do here -- when the signal is received in Thread 1, accept is already blocking, and will continue to block after the signal handler has finished.

  1. What does the answer really mean I should do?
  2. If the Thread 1 signal handler can do something which will cause accept to return immediately, why can't Thread 2 do the same without signals?
  3. Is there another way to do this without signals? I don't want to increase the caveats on the library.
like image 714
spraff Avatar asked May 07 '13 18:05

spraff


2 Answers

Instead of blocking in accept(), block in select(), poll(), or one of the similar calls that allows you to wait for activity on multiple file descriptors and use the "self-pipe trick". All of the file descriptors passed to select() should be in non-blocking mode. One of the file descriptors should be the server socket that you use with accept(); if that one becomes readable then you should go ahead and call accept() and it will not block. In addition to that one, create a pipe(), set it to non-blocking, and check for the read side becoming readable. Instead of calling close() on the server socket in the other thread, send a byte of data to the first thread on the write end of the pipe. The actual byte value doesn't matter; the purpose is simply to wake up the first thread. When select() indicates that the pipe is readable, read() and ignore the data from the pipe, close() the server socket, and stop waiting for new connections.

like image 179
mark4o Avatar answered Oct 10 '22 23:10

mark4o


The accept() call will return with error code EINTR if a signal is caught before a connection is accepted. So check the return value and error code then close the socket accordingly.

If you wish to avoid the signal mechanism altogether, use select() to determine if there are any incoming connections ready to be accepted before calling accept(). The select() call can be made with a timeout so that you can recover and respond to abort conditions.

I usually call select() with a timeout of 1000 to 3000 milliseconds from a while loop that checks for an exit/abort condition. If select() returns with a ready descriptor I call accept() otherwise I either loop around and block again on select() or exit if requested.

like image 25
Amardeep AC9MF Avatar answered Oct 10 '22 22:10

Amardeep AC9MF