iOS Crash with SIGABRT in CFNetwork
Asked Answered
J

2

8

I use custom url protocol in my app by extending NSURLProtocol. It works fine most of the time but I see the following crash being reported via crashlytics. I'm unable to reproduce this myself. The thing that worries me most is, I don't see my app in the stack trace of the crashed thread and so I'm clueless on where to start debugging. Below is the crash report

Thread : Crashed: com.apple.NSURLConnectionLoader
0  libsystem_kernel.dylib         0x3562cc84 __pthread_kill + 8
1  libsystem_pthread.dylib        0x356d0733 pthread_kill + 62
2  libsystem_c.dylib              0x355c4f21 abort + 108
3  libsystem_c.dylib              0x355a47eb __assert_rtn + 302
4  CFNetwork                      0x22b82e45 CFURLProtocol_NS::_protocolInterface_cancelLoad() + 322
5  CFNetwork                      0x22c3740f ___ZN19URLConnectionLoader27_private_ScheduleOriginLoadEPK12NSURLRequestPK20_CFCachedURLResponse_block_invoke_2 + 38
6  CFNetwork                      0x22b66ccd ___ZNK19URLConnectionLoader25withExistingProtocolAsyncEU13block_pointerFvP11URLProtocolE_block_invoke + 16
7  libdispatch.dylib              0x35513bd7 _dispatch_client_callout + 22
8  libdispatch.dylib              0x3551d187 _dispatch_block_invoke$VARIANT$mp + 446
9  CFNetwork                      0x22b66caf RunloopBlockContext::_invoke_block(void const*, void*) + 18
10 CoreFoundation                 0x2326ab51 CFArrayApplyFunction + 36
11 CFNetwork                      0x22b66b97 RunloopBlockContext::perform() + 182
12 CFNetwork                      0x22b66a61 MultiplexerSource::perform() + 216
13 CFNetwork                      0x22b668f9 MultiplexerSource::_perform(void*) + 48
14 CoreFoundation                 0x23319bff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
15 CoreFoundation                 0x233197ed __CFRunLoopDoSources0 + 452
16 CoreFoundation                 0x23317b5b __CFRunLoopRun + 794
17 CoreFoundation                 0x2326b119 CFRunLoopRunSpecific + 520
18 CoreFoundation                 0x2326af05 CFRunLoopRunInMode + 108
19 CFNetwork                      0x22bd8bbf +[NSURLConnection(Loader) _resourceLoadLoop:] + 486
20 Foundation                     0x241291b5 __NSThread__start__ + 1148
21 libsystem_pthread.dylib        0x356cf85b _pthread_body + 138
22 libsystem_pthread.dylib        0x356cf7cf _pthread_start + 110
23 libsystem_pthread.dylib        0x356cd724 thread_start + 8

The intriguing part in the stack trace is these few lines..

4  CFNetwork                      0x22b82e45 CFURLProtocol_NS::_protocolInterface_cancelLoad() + 322
5  CFNetwork                      0x22c3740f ___ZN19URLConnectionLoader27_private_ScheduleOriginLoadEPK12NSURLRequestPK20_CFCachedURLResponse_block_invoke_2 + 38
6  CFNetwork                      0x22b66ccd ___ZNK19URLConnectionLoader25withExistingProtocolAsyncEU13block_pointerFvP11URLProtocolE_block_invoke + 16

I'm able to determine by looking at the stack traces on startLoading and stopLoading methods in NSURLProtocol that ___ZN19URLConnectionLoader27_private_ScheduleOriginLoadEPK12NSURLRequestPK20_CFCachedURLResponse_block_invoke_2 calls startLoading while CFURLProtocol_NS::_protocolInterface_cancelLoad() calls stopLoading due to canceling the request. So, why or how is cancel getting called right after trying to start loading?

Any help is greatly appreciated. Thanks.

Update:

I'm able to reproduce a similar (not exact trace) and I see the following assert..

Assertion failed: (_protocolInstance == nil), function _protocolInterface_startLoad, file /BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork/CFNetwork-758.0.2/Session/LocalSession.mm, line 1341.

and the following is the back trace.

(lldb) bt
* thread #6: tid = 0xe687, 0x34515d24 libsystem_kernel.dylib`__pthread_kill + 8, name = 'com.apple.NSURLConnectionLoader', stop reason = signal SIGABRT
  * frame #0: 0x34515d24 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x345b974a libsystem_pthread.dylib`pthread_kill + 62
    frame #2: 0x344adf40 libsystem_c.dylib`abort + 108
    frame #3: 0x3448d80a libsystem_c.dylib`__assert_rtn + 302
    frame #4: 0x2202de4c CFNetwork`CFURLProtocol_NS::_protocolInterface_startLoad(_CFCachedURLResponse const*) + 324
    frame #5: 0x220e22e6 CFNetwork`___ZN19URLConnectionLoader27_private_ScheduleOriginLoadEPK12NSURLRequestPK20_CFCachedURLResponse_block_invoke_2 + 38
    frame #6: 0x22011cd4 CFNetwork`___ZNK19URLConnectionLoader25withExistingProtocolAsyncEU13block_pointerFvP11URLProtocolE_block_invoke + 16
    frame #7: 0x003a5d72 libdispatch.dylib`_dispatch_client_callout + 22
    frame #8: 0x003ad8d8 libdispatch.dylib`_dispatch_block_invoke + 468
    frame #9: 0x22011cb6 CFNetwork`RunloopBlockContext::_invoke_block(void const*, void*) + 18
    frame #10: 0x22710c80 CoreFoundation`CFArrayApplyFunction + 36
    frame #11: 0x22011b9e CFNetwork`RunloopBlockContext::perform() + 182
    frame #12: 0x22011a68 CFNetwork`MultiplexerSource::perform() + 216
    frame #13: 0x22011900 CFNetwork`MultiplexerSource::_perform(void*) + 48
    frame #14: 0x227bfc3e CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
    frame #15: 0x227bf7c0 CoreFoundation`__CFRunLoopDoSources0 + 344
    frame #16: 0x227bdb9a CoreFoundation`__CFRunLoopRun + 794
    frame #17: 0x22711248 CoreFoundation`CFRunLoopRunSpecific + 520
    frame #18: 0x22711034 CoreFoundation`CFRunLoopRunInMode + 108
    frame #19: 0x22083ee6 CFNetwork`+[NSURLConnection(Loader) _resourceLoadLoop:] + 486
    frame #20: 0x235cc634 Foundation`__NSThread__start__ + 1148
    frame #21: 0x345b8872 libsystem_pthread.dylib`_pthread_body + 138
    frame #22: 0x345b87e6 libsystem_pthread.dylib`_pthread_start + 110
    frame #23: 0x345b6740 libsystem_pthread.dylib`thread_start + 8

Here is a reduced version of my protocol class where MyConnection works very similar to NSURLConnection..

@implementation MyProtocol

- (void) startLoading {
        NSURLRequest *request = self.request;
        self.myConnection = [[MyConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
        NSRunLoop *loop = [NSRunLoop currentRunLoop];
        [self.myConnection scheduleInRunLoop:loop forMode:loop.currentMode];
        [self.myConnection start];
}

- (void) stopLoading {
    if (self.myConnection) {
        [self.myConnection cancel];
        self.myConnection = nil;
    }
}

#pragma mark MyConnectionDelegate


- (void) myConnection:(MyConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
}

- (void) myConnectionDidFinishLoading:(MyConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
}

//..other delegate methods are implemented similarly

Between, the exception code I see is

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  4
Johannesburg answered 27/10, 2015 at 9:54 Comment(13)
are you not able to get line number from Crashlytics? If you are getting then please re-check your code at given line number.Fiorin
__assert_rtn in the first stack trace shows that this is an assert failure, so it's probably the same underlying cause. Can you post some code for your custom NSURLProtocol?Only
Just updated my question with relevant code from MyProtocolJohannesburg
You're running your NSURLConnection on the same thread/runloop that calls startLoading... is that deliberate? I'd have thought you'd need to do the actual url loading on another background thread. How can you handle a stopLoading call properly if that thread is busy downloading?Only
the start method actually creates a new thread and loads the request using the newly created thread. Only the call backs like receivedData, finishedLoading etc are made on the runloop that called startLoading method.Johannesburg
I found lists.apple.com/archives/macnetworkprog/2014/Oct/msg00001.html to be a related issue- but could not get much more information about it. I just verified that the crash mentioned in it still exists in iOS 9+Johannesburg
@Johannesburg Are you using NSURLConnection or NSURLSession? I am having the same crashes only on iOS9, but i cannot reproduce the crash at all :sHaley
I'm using NSURLConnection and yeah I see these crashes on iOS9 and above only.Johannesburg
@Johannesburg How did you recreate the crash? Could it be a nsurlconnection issue? What if you change to NSURLSession? I would like to know the steps for you to reproduce the crash, so I might be able to do something similar for my app.Haley
@Haley I was just trying to make lots and lots of requests and cancel them right after that.. ex. while(1) start request, usleep(), cancel request. I could not find a simple and reliable way to replicate.. The above method ends up with crash after a long time.Johannesburg
@Johannesburg doesn't happen to me with ur while code. Could you try switching the NSURLConnections on that while for NSURLSessions and see if you still get the crash?Haley
@Haley yeah that test don't seem to be good enough to reliably reproduce the issue. Something like this lists.apple.com/archives/macnetworkprog/2014/Oct/msg00001.html results in a similar (not exact) crash thoughJohannesburg
Posted in Apple dev forums to see if any of the Apple guys would care to look into: forums.developer.apple.com/message/83369#83369Johannesburg
T
0

I don't know if you're already doing it but maybe try to force the start and the cancel to be on the same thread?

Try to use -com.apple.CoreData.ConcurrencyDebug 1 for debugging - I'm not sure if it debugs only the CoreData thread but it worths trying.

Temptress answered 30/10, 2015 at 11:0 Comment(3)
I'm trying to create a library with this code. I'm of the same opinion that this is due to some race conditions (more so with the dispatch blocks added from iOS9). The problem in forcing -startLoading and -stopLoading to be on same thread would mean there has to be an assert in stopLoading. So, I'm afraid that would still lead to a crash if our assumption that this crash is due to multiple threads calling start and stop is valid. But I will give it a try if nothing works. Thank youJohannesburg
Let me know.. That sounds interestingTemptress
I tried to make sure the startLoading and stopLoading are called on the same client thread similar to Apple's CustomHTTPProtocol sample. i.e I added assert([NSThread currentThread] == self.clientThread); in stopLoading. But I still hit the assert mentioned in the question and not the assert in stopLoadingJohannesburg
C
0

You might try always performing all your protocol class's work on the main thread using performSelectorOnMainThread (or on some known thread), and see if that helps.

Also, make sure that you tag the request, and that you refuse to handle requests that you have previously tagged, so that you don't get an infinite loop of custom protocol handler creation.

Beyond that, you'll probably need to open a DTS support incident to find out what assertion is failing. Unfortunately, CFNetwork isn't one of the open source components, or else this would be easy to debug.

Clarsach answered 3/12, 2015 at 19:5 Comment(1)
ya, I do tag the requests to avoid infinite loop. I perform the protocol class's work in the caller thread rather than the main thread - which I'm thinking is optimal than using main thread. I did try asking in apple dev forums but I could not get an answer about what that assertion is about.. forums.developer.apple.com/message/83369#83369Johannesburg

© 2022 - 2024 — McMap. All rights reserved.