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 (Cocoa-dev@lists.apple.com)

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 arch...@mail-archive.com

Reply via email to