Hi All!
I'm having an issue how to properly and reliably canceling a NSURLConnection
scheduled on a secondary thread and would really appreciate help.
The actual issue is that sometimes (not always) sending a message from the main
thread - which is basically a higher level cancel message - to the thread where
the connection delegates are scheduled will block infinitively. I'm sending the
message from the main thread via:
[self performSelector:@selector(stopConnectionRunLoop_private)
onThread:self.connectionThread
withObject:nil
waitUntilDone:YES
modes:[NSArray arrayWithObject: NSDefaultRunLoopMode]];
Note the 'YES' for parameter waitUntilDone, which will be explained later.
The main thread blocks infinitively without invoking the selector's message
'stopConnectionRunLoop_private'.
The connection is scheduled in NSDefaultRunLoopMode on the secondary thread, as
usual.
Stack (partial):
----------------
Thread 1 Queue : (null) (main thread)
#0 0x35de454c in __semwait_signal ()
#1 0x35d90f78 in _pthread_cond_wait ()
#2 0x35d90918 in pthread_cond_wait ()
#3 0x3517ed64 in -[NSCondition wait] ()
#4 0x3516910c in -[NSObject(NSThreadPerformAdditions)
performSelector:onThread:withObject:waitUntilDone:modes:] ()
Thread 6, Queue : (null)
#0 0x35d848d8 in select$DARWIN_EXTSN ()
#1 0x3755aa3a in __CFSocketManager ()
#2 0x35de5b4c in _pthread_start ()
#3 0x35dd77ac in thread_start ()
Thread 7, Queue : (null)
#0 0x35d5b400 in semaphore_wait_trap ()
#1 0x35d91460 in semaphore_wait ()
#2 0x35e5f3cc in _dispatch_semaphore_wait_slow ()
A more detailed explanation:
----------------------------
>From the main thread, I invoke this method:
- (void) cancelButtonTapped {
[self cancel];
}
where self is a controller and the connection delegate. -cancel is implemented
as follows:
- (void) cancel {
[self cancelConnectionWaitUntilDone:YES];
}
"WaitUntilDone:YES" is a requirement which should guarantee that all delegate
methods have finished and the secondary thread has terminated (note, connection
delegates execute on the secondary thread). Otherwise, I would possibly risk a
race condition due to accessing ivars from the secondary thread and the main
thread.
-cancelConnectionWaitUntilDone: is implemented as follows, and yes this seems
quite elaborated, but I haven't found an easy way to accomplish this, till now:
- (void) cancelConnectionWaitUntilDone:(BOOL)wait
{
if (self.connection) {
NSLog(@"Attempt to cancel the connection ...");
[self.connection cancel];
self.connection = nil;
NSLog(@"... cancel returned.");
}
else {
NSLog(@"No connection to cancel");
}
[self stopConnectionRunLoopWaitUntilDone:wait]; // here it may block
infinitively occasionally
}
Note: the reason for sending a message to the Run Loop is to cause the Run Loop
to exit, which in turn can be utilized to check the flag 'runLoopDone_' which
causes the outer loop to exit, and eventually causes the secondary thread to
exit. Please see the handling of the Run Loop (code fragment) below:
runLoopDone_ = NO;
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port]
forMode:NSDefaultRunLoopMode];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate
dateWithTimeIntervalSinceNow:10]];
if (runLoopDone_) {
NSLog(@"Exit RunLoop.");
}
} while (!runLoopDone_);
- (void) stopConnectionRunLoopWaitUntilDone:(BOOL)wait
{
NSLog(@"stopConnectionRunLoopWaitUntilDone with thread %@",
self.connectionThread);
if (self.connectionThread == nil) {
return;
}
[self performSelector:@selector(stopConnectionRunLoop_private)
onThread:self.connectionThread
withObject:nil
waitUntilDone:wait
modes:[NSArray arrayWithObject: NSDefaultRunLoopMode]];
}
- (void) stopConnectionRunLoop_private {
if (self.connection) {
NSLog(@"cannot stop Run Loop: connection is still active");
return;
}
runLoopDone_ = YES;
}
Log:
---
2012-04-25 08:59:07.488 Test[2165:307] Attempt to cancel the connection ...
2012-04-25 08:59:07.532 Test[2165:307] ... cancel returned.
2012-04-25 08:59:07.697 Test[2165:307] stopConnectionRunLoopWaitUntilDone with
thread <NSThread: 0x151de0>{name = (null), num = 14}
Thanks for help!
Regards
Andreas
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]