Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #156: Prevents crash in handleEvent method #169

Merged
merged 20 commits into from
Feb 19, 2016

Conversation

jleandroperez
Copy link
Contributor

We've been seeing crashes with the stack trace posted below. Our app is crashing during a block_copy operation, performed inside the method [SRWebSocket stream:handleEvent:].

Although i've been unable to directly reproduce this, i've added a couple safety measures that would prevent this from happening.

Suspected scenario:

  • NSInputStream executes stream:handleEvent: in a BG thread.
  • A the exact same time, SRWebSocket instance gets dealloc'ed.
  • When stream:handleEvent: calls the dispatch_async routine, reference to self is no longer valid.

Workaround:

  1. Updated stream:handleEvent: method: part of the code was moved to a second method, with a self-strong reference, stored in the stack.
  2. Replaced 'self' with 'weakSelf', when appropriate.
  3. NSStream delegate(s) are now being cleaned up in SocketRocket's NSRunLoop.

Any feedback will be very welcome. Thanks in advance, and congratulations on building this awesome project!

Crash Stack Trace:

Crashed Thread:  3

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLT

Thread 0:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib          0x00007fff894a9a1a mach_msg_trap + 10
1   libsystem_kernel.dylib          0x00007fff894a8d18 mach_msg + 64
2   com.apple.CoreFoundation        0x00007fff94743fc5 __CFRunLoopServiceMachPort + 181
3   com.apple.CoreFoundation        0x00007fff947435e9 __CFRunLoopRun + 1161
4   com.apple.CoreFoundation        0x00007fff94742f25 CFRunLoopRunSpecific + 309
5   com.apple.HIToolbox             0x00007fff912eda0d RunCurrentEventLoopInMode + 226
6   com.apple.HIToolbox             0x00007fff912ed7b7 ReceiveNextEventCommon + 479
7   com.apple.HIToolbox             0x00007fff912ed5bc _BlockUntilNextEventMatchingListInModeWithFilter + 65
8   com.apple.AppKit                0x00007fff8fc603de _DPSNextEvent + 1434
9   com.apple.AppKit                0x00007fff8fc5fa2b -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
10  com.apple.AppKit                0x00007fff8fc53b2c -[NSApplication run] + 553
11  com.apple.AppKit                0x00007fff8fc3e913 NSApplicationMain + 940
12  libdyld.dylib                   0x00007fff8b3c95fd start + 1

Thread 1:: Dispatch queue: com.apple.libdispatch-manager
0   libsystem_kernel.dylib          0x00007fff894ae662 kevent64 + 10
1   libdispatch.dylib               0x00007fff91d0743d _dispatch_mgr_invoke + 239
2   libdispatch.dylib               0x00007fff91d07152 _dispatch_mgr_thread + 52

Thread 2:
0   libsystem_kernel.dylib          0x00007fff894a9a1a mach_msg_trap + 10
1   libsystem_kernel.dylib          0x00007fff894a8d18 mach_msg + 64
2   com.apple.CoreFoundation        0x00007fff94743fc5 __CFRunLoopServiceMachPort + 181
3   com.apple.CoreFoundation        0x00007fff947435e9 __CFRunLoopRun + 1161
4   com.apple.CoreFoundation        0x00007fff94742f25 CFRunLoopRunSpecific + 309
5   com.apple.AppKit                0x00007fff8fe0016e _NSEventThread + 144
6   libsystem_pthread.dylib         0x00007fff907cc899 _pthread_body + 138
7   libsystem_pthread.dylib         0x00007fff907cc72a _pthread_start + 137
8   libsystem_pthread.dylib         0x00007fff907d0fc9 thread_start + 13

Thread 3 Crashed:
0   libobjc.A.dylib                 0x00007fff88bff7a2 objc_retain + 18
1   libsystem_blocks.dylib          0x00007fff89371580 _Block_copy_internal + 308
2   libdispatch.dylib               0x00007fff91d05546 _dispatch_Block_copy + 43
3   libdispatch.dylib               0x00007fff91d0b13f dispatch_async + 17
4   com.simperium.Simperium-OSX     0x0000000107fcd3aa -[SRWebSocket stream:handleEvent:] + 801
5   com.apple.CoreFoundation        0x00007fff94792c81 _signalEventSync + 385
6   com.apple.CoreFoundation        0x00007fff94792ac8 _cfstream_solo_signalEventSync + 328
7   com.apple.CoreFoundation        0x00007fff9479293f _CFStreamSignalEvent + 623
8   com.apple.CFNetwork             0x00007fff949ba16e CoreWriteStreamCFStreamSupport::coreStreamWriteEvent(__CoreWriteStream*, unsigned long) + 102
9   com.apple.CFNetwork             0x00007fff949ba0dd CoreWriteStreamClient::coreStreamEventsAvailable(unsigned long) + 53
10  com.apple.CFNetwork             0x00007fff94aeda65 CoreStreamBase::_callClientNow(CoreStreamClient*) + 53
11  com.apple.CFNetwork             0x00007fff949e3558 ___ZN14CoreStreamBase34_streamSetEventAndScheduleDeliveryEmh_block_invoke + 33
12  com.apple.CFNetwork             0x00007fff949d32ec ___ZNK17CoreSchedulingSet13_performAsyncEPKcU13block_pointerFvvE_block_invoke + 25
13  com.apple.CoreFoundation        0x00007fff9471db44 CFArrayApplyFunction + 68
14  com.apple.CFNetwork             0x00007fff949d31cb RunloopBlockContext::perform() + 115
15  com.apple.CFNetwork             0x00007fff949d3073 MultiplexerSource::perform() + 269
16  com.apple.CFNetwork             0x00007fff949d2ea2 MultiplexerSource::_perform(void*) + 72
17  com.apple.CoreFoundation        0x00007fff94752661 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
18  com.apple.CoreFoundation        0x00007fff94743d12 __CFRunLoopDoSources0 + 242
19  com.apple.CoreFoundation        0x00007fff9474349f __CFRunLoopRun + 831
20  com.apple.CoreFoundation        0x00007fff94742f25 CFRunLoopRunSpecific + 309
21  com.apple.Foundation            0x00007fff8d0e1adc -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 253
22  com.simperium.Simperium-OSX     0x0000000107fce528 -[_SRRunLoopThread main] + 291
23  com.apple.Foundation            0x00007fff8d0df76b __NSThread__main__ + 1318
24  libsystem_pthread.dylib         0x00007fff907cc899 _pthread_body + 138
25  libsystem_pthread.dylib         0x00007fff907cc72a _pthread_start + 137
26  libsystem_pthread.dylib         0x00007fff907d0fc9 thread_start + 13

@marianoabdala
Copy link

Hi @jleandroperez, so have the crash reports stopped after applying this? Thanks!

@jleandroperez
Copy link
Contributor Author

@marianoabdala So far i have not received any further crash reports. I'll keep you posted (if it happens ever again!).

taiyangc added a commit to quatanium/SocketRocket that referenced this pull request Jun 4, 2014
@lukeredpath
Copy link
Contributor

Any updates on getting this merged?

@jtreanor
Copy link

I am receiving crash reports just like these in my app. Is there any possibility of getting this merged?

@frranck
Copy link

frranck commented Apr 29, 2015

Same here, why is it not merged ?

@dgatwood
Copy link

NSStream doesn't retain its delegate. If the code isn't retaining the delegate elsewhere, it will become a zombie. It is not safe to allow the delegate to go away until you completely tear down the stream. So if I'm reading the description of the "workaround" correctly, it isn't a workaround so much as a bug fix.

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file looks like it should be in your .gitignore. Please remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that, removing right away!

@mikelikespie
Copy link
Contributor

Sorry for the long delay. I'd rather avoid using @synchronized. Could we make it just dispatch that work on the _workQueue?

Thanks,
Mike

@jleandroperez
Copy link
Contributor Author

On the contrary @mikelikespie, thanks for the review (+ for opensourcing this project, we use it quite a lot!).

I'm afraid that simply replacing the @synchronized blocks with a dispatch on the _workQueue might not do the trick, since we really need to execute the _cleanupSelfReference method on the same thread (/runloop) as the NSStream instances.

Perhaps we could switch to NSLock instead?.

Thank you sir!

- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream
{
__strong typeof(self) strongSelf = self;
((void)strongSelf);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self is always strong. Don't need this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right Mike. Pre-arc school says 'that's not accurate', but post arc school says otherwise. Thank you, updating =)

@mikelikespie
Copy link
Contributor

@jleandroperez I feel like you could do most of it by dispatching on the _workQueue (at least making sure you only schedule the cleanup once). I think you can call close from any thread too.

@jleandroperez
Copy link
Contributor Author

Hello there @mikelikespie!

I'm afraid that moving the cleanup code to the _workQueue might not do the trick, since we really need to execute the _cleanupSelfReference method on the same thread (/runloop) as the NSStream instances.

@mikelikespie
Copy link
Contributor

@jleandroperez Can you elaborate on why _cleanupSelfReference needs to be called on the same thread please?

@dfed
Copy link
Contributor

dfed commented Nov 14, 2015

Ping @jleandroperez :)

@jtreanor
Copy link

I have described an alternative solution to this here: #156 (comment).

I'd love to hear any thoughts on it 😃

@jleandroperez
Copy link
Contributor Author

Hello @mikelikespie and @dfed,

Apologies about the delay gentleman!. The reason why the cleanup sequence needs to be called on the same thread used by NSStream was kindly pointed out by @dgatwood up here.

NSStream's delegate property is not weak, but assign. Invalidating that pointer from elsewhere can explain the race condition we've been seeing (and the associated crashes).

That's the reason why, i'm afraid, moving the cleanup call back to the _workQueue queue neutralizes the fix.

I'll merge the conflicts shortly (and will address the strongSelf comment as well.

And again, thank you both for sharing this project!

@jleandroperez
Copy link
Contributor Author

@mikelikespie ready for another round sir!. All comments have been addressed.

In the aims of making the reviewer's life happier, the safeHandleEvent method has an extra tab. (Otherwise, github diff has a very rough time spotting just the delta).

(If) this PR is approved, i'd be happy to remove that tab (just temporarily there).

@jtreanor i'm not a big fan of Singletons, but i'll definitely take a look at your solution. Sounds less invasive, thanks for sharing!

@maxovtsin
Copy link

Are you planning merge it? This request potentially can fix very popular crash.

@dfed
Copy link
Contributor

dfed commented Feb 17, 2016

Hey folks! Our turn to be slow. We've got a merge/reviewfest this Thursday and we'll take a look then.

@jleandroperez
Copy link
Contributor Author

Thank you @dfed, much appreciated!

@dfed
Copy link
Contributor

dfed commented Feb 19, 2016

This looks reasonable to me. Thanks for the poke folks. Merging!

dfed added a commit that referenced this pull request Feb 19, 2016
Issue #156: Prevents crash in handleEvent method
@dfed dfed merged commit 541f7b6 into facebookincubator:master Feb 19, 2016
@jleandroperez
Copy link
Contributor Author

Pure awesomeness, thanks a lot @dfed!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants