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

Avoid premature completion of prefetcher if request fails #751

Merged
merged 2 commits into from
Jun 18, 2014
Merged

Avoid premature completion of prefetcher if request fails #751

merged 2 commits into from
Jun 18, 2014

Conversation

robertmryan
Copy link
Contributor

This addresses a number of issues, namely:

  • This resolves bug in prefetcher that could result in premature notification of completion if one of the requests failed, as discussed in issue SDWebImagePrefetcher wrong completion block condition if skipped any images  #715.
  • This attempts to make documentation in headers a little less ambiguous regarding the intent of the finishedCount parameter, making it clear that it includes both successful and unsuccessful requests.
  • Unrelated, also fix race condition in SDWebimageDownloader.

While exceedingly unlikely, the old construct introduced potential race condition where it checked wself first, and assigned sself second. This now assigns sself first, and then checks that, which is the correct pattern, already used elsewhere in SDWebImage.
…uld finish prematurely if any requests were skipped.

Also adjusted documentation in headers to make it clear that the `finishedCount` includes both successful and unsuccessful requests.
@robertmryan
Copy link
Contributor Author

@rs

You might want to rebuild the appledoc-based class framework documentation to reflect the header documentation changes.

@bpoplauschi bpoplauschi modified the milestones: Future, 3.7.0 Jun 17, 2014
bpoplauschi added a commit that referenced this pull request Jun 18, 2014
Avoid premature completion of prefetcher if request fails
@bpoplauschi bpoplauschi merged commit 9dcd68d into SDWebImage:master Jun 18, 2014
@bpoplauschi
Copy link
Member

@rs Please review as well, especially the else if (self.finishedCount == self.requestedCount) condition. Looks legit to me, but I'm still not experienced with this code.

@rs
Copy link
Member

rs commented Jun 18, 2014

The else if (self.finishedCount == self.requestedCount) looks ok, but I'm wondering about the weak ref changed to strong ref. Could you please elaborate on this @robertmryan?

@robertmryan
Copy link
Contributor Author

Technically, in this case, it doesn't matter, because the weakSelf/strongSelf pattern isn't necessary at all in this particular scenario. But if you're going to do what this code is doing, affectionately known as the "weakSelf/strongSelf dance", you might as well avoid race conditions that often plague multithreaded code.

First, to illustrate the recommended pattern, look at Transitioning to ARC Release Notes, there is a section called "Use Lifetime Qualifiers to Avoid Strong Reference Cycles", in which there is an example that says:

For non-trivial cycles, however, you should use:

MyViewController *myController = [[MyViewController alloc] init…];

// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler =  ^(NSInteger result) {
    MyViewController *strongMyController = weakMyController;
    if (strongMyController) {
        // ...
        [strongMyController dismissViewControllerAnimated:YES completion:nil];
        // ...
    }
    else {
        // Probably nothing...
    }
};

Note, you assign the weak object reference to a strong variable first, and then check to see if that strong variable is non-nil.

If you do it the other way around (check to see if the weak variable is non-nil, and only then set the strong variable), there is a race condition, namely that it's theoretically possible for the weak variable to become nil between the time you check to see that it's non-nil, and the time you assign the strong variable.

Usually, when you do this weakSelf/strongSelf dance, you're doing it because you want to ensure that strongSelf is not nil. So my change achieves that. It's not critical in this particular situation, but I just break out in hives anytime I see code that introduces theoretical race conditions. No offense was intended by my edit.

@bpoplauschi
Copy link
Member

@robertmryan thanks for the detailed explanation. You are 100% correct.

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.

3 participants