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

Weakify self's in dispatch_async's #1426

Closed
wants to merge 5 commits into from
Closed

Weakify self's in dispatch_async's #1426

wants to merge 5 commits into from

Conversation

paulb777
Copy link
Member

Fix #1317. Specifically address @baolocdo's comment at #1317 (comment)

@paulb777 paulb777 added this to the M29 milestone Jun 19, 2018
Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

The reason why we get all those implicit retain warnings is that self is implicitly strongly retained in blocks like this.

What weakifying does it make it possible for the block to outlive the object that submitted the object into the queue. That doesn’t seem like a goal here so this seems like the wrong change.

@paulb777
Copy link
Member Author

Thanks @wilhuff - it seems the backtraces at #1317 (comment) are because nothing is pointing at the self's - not that the self has been deallocated.

@paulb777 paulb777 closed this Jun 19, 2018
@paulb777 paulb777 deleted the pb-weakify branch June 19, 2018 23:39
@paulb777 paulb777 restored the pb-weakify branch June 20, 2018 04:48
@paulb777
Copy link
Member Author

@wilhuff I was thinking more about this and isn't the reason that these accesses are zombies would be because of the extra retain in the dispatch_async block? If that retain is removed, then the objects can be reclaimed when their references are released.

@paulb777 paulb777 reopened this Jun 20, 2018
dispatch_async(_queue, ^{
[self->_objects removeObjectForKey:key];
if (weakSelf) {
[self->_objects removeObjectForKey:key];
Copy link
Member

Choose a reason for hiding this comment

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

Are there any problems with [weakSelf->_objects removeObjectForKey:key] here instead?

Copy link

@baolocdo baolocdo left a comment

Choose a reason for hiding this comment

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

Thanks

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

@paulb777 Zombie objects are those whose retain counts have gone to zero but are still getting messages, usually through an unsafe_unretained pointer. The retains of self caused by dispatch_async block would prevent zombies.

I have some detailed comments on the implementation you're proposing, but at a higher level I don't think this addresses the root cause which is that somehow methods are being invoked on objects that don't otherwise already have strong references.

dispatch_async(_queue, ^{
[self->_objects removeObjectForKey:key];
if (weakSelf) {
typeof(self) strongSelf = weakSelf;
Copy link
Contributor

@wilhuff wilhuff Jun 20, 2018

Choose a reason for hiding this comment

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

This is still racy. The pattern to use is

typeof(self) strongSelf = weakSelf;
if (strongSelf) {
  // Do interesting things with strongSelf.
}

Or

typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
  return;
}

In other words: the if should be testing the strong reference recovered from the weak pointer.

As you've written it it's possible for weakSelf to be allocated on the first line but deallocated on the second (before the assignment).

@@ -146,13 +146,13 @@ - (NSString *)postURL:(NSURL *)url
sessionIDFromAsyncPOSTRequest:request
completionHandler:^(NSHTTPURLResponse *response, NSData *data,
NSString *sessionID, NSError *error) {
FIRNetwork *strongSelf = weakSelf;
if (!strongSelf) {
if (!weakSelf) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't do what you want it to do. Between this point and the point where you use it next the object pointed to by weakSelf can still be deallocated. That effectively makes this check worthless.

Since the removal already does its work in another queue, there's little reason to bounce it onto the user callback queue. A better arrangement in this callback block would be:

FIRNetwork *__strong strongSelf = weakSelf;
if (!strongSelf) {
  return;
}
if (sessionID.length) {
  [strongSelf->_requests removeObjectForKey:sessionID];
}

if (handler) {
  dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
  dispatch_async(queueToDispatch, ^{
    handler(response, data, error);
  });
}

@paulb777
Copy link
Member Author

@wilhuff Thanks for the high and low level feedback. The implementation issues are addressed. I'll dig deeper on the high level questions this afternoon.

@morganchen12
Copy link
Contributor

IIRC a retain call in a block could still cause a zombies crash if the object being retained had been deallocated prior to block capture, but I don't think the weak pointer would help in this case.

@paulb777
Copy link
Member Author

@morganchen12 and I reviewed the zombie report and are convinced that this PR does not solve a real problem - agreeing with @wilhuff. It is not possible for self to be deallocated before these blocks.

@paulb777 paulb777 closed this Jun 21, 2018
@paulb777 paulb777 deleted the pb-weakify branch June 21, 2018 23:40
@firebase firebase locked and limited conversation to collaborators Oct 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Some crashes with firebase sdk
6 participants