After changing my NSURLConnection client code to use operation queues instead of runloop-based delegate calls, I'm running into a repeatable deadlock; but only on iOS (5.1, including the simulator), not Mac OS X 10.7.3. _connection = [[NSURLConnection alloc] initWithRequest: _request delegate: self startImmediately: NO]; [_connection setDelegateQueue: [NSOperationQueue currentQueue]]; [_connection start]; The main thread then hangs forever while attempting to call the delegate. (See backtrace below.) What I think happens is that it makes a block to call the delegate method, pushes that block onto the queue as an operation, and then waits for it to finish; but the main thread’s operation queue is sequential and already invoked, so it will never get to that operation until the current one finishes. I’m kind of mystified why this doesn’t happen on Mac OS. Is the delegate dispatching code different on the two platforms? And how should I work around this? Should I avoid setting the delegate queue when on the main thread? Any other special-case gotchas like this I should know about? :/ —Jens (gdb) bt #0 0x972cb83e in __psynch_cvwait () #1 0x90a25e21 in _pthread_cond_wait () #2 0x909d642c in pthread_cond_wait$UNIX2003 () #3 0x00bea8ec in -[__NSOperationInternal waitUntilFinished] () #4 0x00bea84e in -[NSOperation waitUntilFinished] () #5 0x00c9fefe in -[NSURLConnectionInternalConnection invokeForDelegate:] () #6 0x00c9ee3f in -[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:] () #7 0x00c9efc5 in -[NSURLConnectionInternal _withActiveConnectionAndDelegate:] () #8 0x00be35bb in _NSURLConnectionDidReceiveResponse () #9 0x03d39887 in URLConnectionClient::_clientSendDidReceiveResponse () #10 0x03e07710 in URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload () #11 0x03e07861 in URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload () #12 0x03d31120 in URLConnectionClient::processEvents () #13 0x03e07117 in non-virtual thunk to URLConnectionInstanceData::multiplexerClientPerform() () #14 0x03d30fbf in MultiplexerSource::perform () #15 0x0123894f in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #16 0x0119bb43 in __CFRunLoopDoSources0 () #17 0x0119b424 in __CFRunLoopRun () #18 0x0119ad84 in CFRunLoopRunSpecific () #19 0x0119ac9b in CFRunLoopRunInMode () #20 0x00be340f in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] () #21 0x00080ed8 in Test_TDMultipartDownloader () at /Volumes/HardDisk/Couchbase/TouchDB/Source/TDMultipartDownloader.m:280 #22 0x000664a6 in RunTestCase (test=0xed904) at /Volumes/HardDisk/Couchbase/TouchDB/vendor/MYUtilities/Test.m:53 #23 0x00066983 in RunTestCases (argc=11, argv=0xbffff5bc) at /Volumes/HardDisk/Couchbase/TouchDB/vendor/MYUtilities/Test.m:128 #24 0x00002b6e in main (argc=11, argv=0xbffff5bc) at /Volumes/HardDisk/Couchbase/TouchDB/Demo-iOS/main.m:17
On 21 Mar 2012, at 18:51, Jens Alfke wrote: > I’m kind of mystified why this doesn’t happen on Mac OS. Is the delegate dispatching code different on the two platforms?My experience is that iOS is much less forgiving about over committing a queue than Mac OS X is, which makes it must easier to deadlock. Share and Enjoy
On Mar 22, 2012, at 3:04 AM, Quinn The Eskimo! wrote: > My experience is that iOS is much less forgiving about over committing a queue than Mac OS X is, which makes it must easier to deadlock.I’m not sure what you mean by ‘over committing’; could you elaborate? It looks as though any queue misuse here is caused by CFNetwork itself. It’s first calling out to the runloop on the original thread, and then from there trying to (synchronously) run an NSOperation on the queue. There’s no reason for it to do the first part — it could post and wait for that NSOperation from any thread — and in fact it’s very likely to deadlock if the dispatch queue is tied to the thread that originated the connection. If you also consider my later message, where I worked around this issue and found that the delegate never gets called at all (even if it’s not using the main thread’s queue), it starts to look as though the queue-based delegate mode in NSURLConnection is rather broken in iOS. My task for today, it seems, is to rip out the changes I made to use it, and instead spin up my own NSThread with a runloop. —Jens
> I’m not sure what you mean by ‘over committing’; could you elaborate? I presume he means the use of more threads than processor cores to service concurrent queues, such that multiple parallel dispatch_syncs (or otherwise blocking from within a queue) won't bring everything to a grinding halt. iOS is indeed much more susceptible to this, though I don't think it's relevant to your problem here.
On Mar 22, 2012, at 9:17 AM, Wade Tregaskis wrote: >> I’m not sure what you mean by ‘over committing’; could you elaborate? > > I presume he means the use of more threads than processor cores to service concurrent queues, such that multiple parallel dispatch_syncs (or otherwise blocking from within a queue) won't bring everything to a grinding halt. iOS is indeed much more susceptible to this, though I don't think it's relevant to your problem here.I don’t think so either; the main thread’s operation queue seems to be purely sequential. And the operation queue I created is also sequential (max-tasks is 1). —Jens