0

I have an app where the network activity is done in its separate thread (and the network thread continuously gets data from the server and updates the display - the display calls are made back on the main thread). When the user logs out, the main thread calls a disconnect method on the network thread as follows:

[self performSelector:@selector(disconnectWithErrorOnNetworkThread:) onThread:nThread withObject:e waitUntilDone:YES];

This selector gets called most of the time and everything works fine. However, there are times (maybe 2 out of ten times) that this call never returns (in other words the selector never gets executed) and the thread and the app just hang. Anyone know why performSelector is behaving erratically?

Please note that I need to wait until the call gets executed, that's why waitUntilDone is YES, so changing that to NO is not an option for me. Also the network thread has its run loop running (I explicitly start it when the thread is created).

Please also note that due to the continuous nature of the data transfer, I need to explicitly use NSThreads and not GCD or Operations queues.

R.S
  • 321
  • 3
  • 15
  • 1
    Have you paused the debugger to see what code is running when the disconnect fails? It is likely it is in the middle of a data transfer and therefore it will not cancel until the next run loop when that chunk of data is sent or received. – Joe Nov 05 '12 at 20:39
  • Joe, the data is received in small exchanges so the selector should be called once the current data transfer ends. the run loop should schedule the disconnect after it finishes the current data transfer. And this is what happens roughly 80% of the time, just not all the time. As for the debugger, it is paused on the disconnectWithErrorOnNetworkThread call, and just waiting. also the actual disconnectWithErrorOnNetworkThread method never gets entered (I have a NSLog at the beginning of that method that is never called). – R.S Nov 05 '12 at 20:43
  • 2
    I think @Joe was asking what's happening in the target thread (`nThread`), not what's happening in the calling thread. If the target thread never gets back to its main loop, it can never perform the selector for you. – abarnert Nov 05 '12 at 20:47
  • If you are implementing your own threading, I assume you are implementing your own locking techniques. Is it possible that you are deadlocking? – Joe Nov 05 '12 at 20:51
  • ah, yes, that makes sense - I checked and nThread is making a call to the main thread, so it probably is a deadlock. I am trying to upload screenshots but I dont see an option to do so. – R.S Nov 05 '12 at 21:04

1 Answers1

3

That'll hang if:

  • it is attempting to perform a selector on the same thread the method was called from

  • the call to perform the selector is to a thread from which a synchronous call was made that triggered the perform selector

When your program is hung, have a look at the backtraces of all threads.

Note that when implementing any kind of networking concurrency, it is generally really bad to have synchronous calls from the networking code into the UI layers or onto other threads. The networking thread needs to be very responsive and, thus, just like blocking the main thread is bad, anything that can block the networking thread is a bad, too.

Note also that some APIs with callbacks don't necessarily guarantee which thread the callback will be delivered on. This can lead to intermittent lockups, as described.

Finally, don't do any active polling. Your networking thread should be fully quiescent unless some event arrives. Any kind of looped polling is bad for battery life and responsiveness.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • You say a hang will occur if "it is attempting to perform a selector on the same thread the method was called from". But the docs say "If the current thread and target thread are the same, and you specify `YES` for this parameter, the selector is performed immediately on the current thread" which in my experience, I've found to be true – Mike Abdullah Nov 06 '12 at 17:55
  • That is correct, but not all synchronous invocation mechanisms protect you from such a hang. `dispatch_sync()`, for example. And you can also get hung up in nested run loops, when dispatching in specific modes, for example. – bbum Nov 06 '12 at 18:00
  • @Joe, you had the first correct answer, but SO doesn't let me mark your comment as the answer, so upvoting you. bbum: your answer is more comprehensive, and as you guessed, the main thread was blocking the network thread (it was a really bad idea to begin with), and vice versa. It was an easy fix. Thanks to both of you for the help! – R.S Nov 09 '12 at 19:46