Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wake up thread blocked on accept() call

Sockets on Linux question

I have a worker thread that is blocked on an accept() call. It simply waits for an incoming network connection, handles it, and then returns to listening for the next connection.

When it is time for the program to exit, how do I signal this network worker thread (from the main thread) to return from the accept() call while still being able to gracefully exit its loop and handle its cleanup code.

Some things I tried:

  1. pthread_kill to send a signal. Feels kludgy to do this, plus it doesn't reliably allow the thread to do it's shutdown logic. Also makes the program terminate as well. I'd like to avoid signals if at all possible.

  2. pthread_cancel. Same as above. It's a harsh kill on the thread. That, and the thread may be doing something else.

  3. Closing the listen socket from the main thread in order to make accept() abort. This doesn't reliably work.

Some constraints:

If the solution involves making the listen socket non-blocking, that is fine. But I don't want to accept a solution that involves the thread waking up via a select call every few seconds to check the exit condition.

The thread condition to exit may not be tied to the process exiting.

Essentially, the logic I am going for looks like this.

void* WorkerThread(void* args) {     DoSomeImportantInitialization();  // initialize listen socket and some thread specific stuff      while (HasExitConditionBeenSet()==false)     {         listensize = sizeof(listenaddr);         int sock = accept(listensocket, &listenaddr, &listensize);          // check if exit condition has been set using thread safe semantics         if (HasExitConditionBeenSet())         {             break;         }          if (sock < 0)         {             printf("accept returned %d (errno==%d)\n", sock, errno);         }         else         {             HandleNewNetworkCondition(sock, &listenaddr);         }     }      DoSomeImportantCleanup(); // close listen socket, close connections, cleanup etc..     return NULL; }  void SignalHandler(int sig) {     printf("Caught CTRL-C\n"); }  void NotifyWorkerThreadToExit(pthread_t thread_handle) {     // signal thread to exit }  int main() {     void* ptr_ret= NULL;     pthread_t workerthread_handle = 0;      pthread_create(&workerthread, NULL, WorkerThread, NULL);      signal(SIGINT, SignalHandler);      sleep((unsigned int)-1); // sleep until the user hits ctrl-c      printf("Returned from sleep call...\n");      SetThreadExitCondition(); // sets global variable with barrier that worker thread checks on      // this is the function I'm stalled on writing     NotifyWorkerThreadToExit(workerthread_handle);      // wait for thread to exit cleanly     pthread_join(workerthread_handle, &ptr_ret);      DoProcessCleanupStuff();  } 
like image 502
selbie Avatar asked Mar 21 '10 07:03

selbie


People also ask

How do I unblock a user I accept?

One hacky trick to unblock accept(2) is to actually connect(2) to the listening end from your other thread. Flip some flag indicating it's time to stop the loop, connect(2) , close(2) the connecting socket. That way the accept(2) -ing thread would know to close the socket and shut itself down.

What is accept system call?

The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket.


1 Answers

Close the socket using the shutdown() call. This will wake up any threads blocked on it, while keeping the file descriptor valid.

close() on a descriptor another thread B is using is inherently hazardous: another thread C may open a new file descriptor which thread B will then use instead of the closed one. dup2() a /dev/null onto it avoids that problem, but does not wake up blocked threads reliably.

Note that shutdown() only works on sockets -- for other kinds of descriptors you likely need the select+pipe-to-self or cancellation approaches.

like image 98
jilles Avatar answered Sep 20 '22 19:09

jilles