1

I'm adapting Linux networking code to also run on iOS. It uses POSIX listen() and accept() calls to listen for and open socket connections.

I have dispatch queues that listen for and accept new connections, and then other queues that manage open connections.

The jobs running on all the queues have run loops that terminate when a global quit flag is set to true.

The listen and accept calls are blocking calls, and if I need to shut down the app, the queues running those calls will sometimes stall for a minute or two, blocked. (Usually in the accept call.)

Is there a way to terminate the processes that are blocked? There are discussion threads (not POSIX threads) like this one here on SO that say that there is not a way built into GCD. I guess I could figure out which thread is currently blocked and use POSIX calls to kill that thread, but that might cause problems with GCD (I don't know how GCD would handle it if one or more of its worker threads was terminated.)

Has anybody attempted that approach?

Community
  • 1
  • 1
Duncan C
  • 128,072
  • 22
  • 173
  • 272

1 Answers1

3

You definitely should not try to kill a GCD thread. They don't belong to you; they belong to GCD.

The best way to cancel an accept in a multi-threaded application during shutdown is to close the socket. This is particularly useful during shutdown because there are no race-conditions around socket reuse (if the file descriptor might be reused, you have to make sure you're closing the right socket).

If it is impossible to close the socket, you can send a signal to the thread with pthread_kill and an empty signal handler to cause the accept to fail with EINTR. But then you shouldn't use GCD for this. GCD actually blocks all maskable signals so it won't work, but if it did, it would be a bad idea. GCD threads are part of a pool and are reused.

All that said, if at all possible, I'd really recommend converting this code to use CFStream rather than directly calling accept. It's going to integrate with the system much better. For an excellent example of integrating raw sockets, CFStream and GCD, see Robbie Hanson's CocoaAsyncSocket. While this is in ObjC, CFStream is a Core Foundation object and can be managed entirely in C. You may find that CocoaAsyncSocket can replace all your code, however.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Closing the socket did the trick. Thanks. After setting a global quit flag that my jobs check for, I wait a few seconds, and if the jobs haven't terminated, I close the sockets they are serving. That causes the accept and listen functions to return immediately, just as you suggested. Thanks. – Duncan C Dec 26 '15 at 03:58
  • This is cross-platform code shared with Linux, so the less iOS-specific changes we make, the better. As for ease of use, I'd rather use Cocoa touch classes if all else was equal. I find Core Foundation a bit of a pain to deal with (Even though I'm a C programmer from way back, these days I prefer to work in Objective-C or Swift where possible. This is just a case where it **isn't** possible.) – Duncan C Dec 26 '15 at 04:02