-
-
Notifications
You must be signed in to change notification settings - Fork 6k
Try to move the SDWebImageDownloaderOperation dataTask property initialization into synchronized lock to avoid thread-safe issue #3856
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
Conversation
WalkthroughThis change modifies Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10–15 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…alization into synchronized lock to avoid thread-safe issue
29a3921 to
ca209c3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
SDWebImage/Core/SDWebImageDownloaderOperation.m (1)
235-255: Consider recheckingisCancelledbefore assigningself.dataTask/executingand resumingThere is still a small window between the first and second
@synchronized(self)blocks wherecancelcan run:
starthas already createddataTaskand left the first@synchronized(self).- Another thread calls
cancel, which (viacancelInternalandreset) can:
- Fire completion blocks and clear
callbackTokens.- Invalidate the session.
- Leave the operation in a cancelled state (
isCancelled == YES) but withisExecuting == NOandisFinished == NO.After that, the second
@synchronized(self)instartwill still execute, setself.dataTask = dataTask; self.executing = YES;, and[self.dataTask resume];, effectively “reviving” an operation that was just cancelled and had its callbacks fired.This is a pre‑existing race, not introduced by this PR, but it becomes more visible now that
dataTaskhandling is tightened. To align cancel semantics with expectations (cancel-before-or-during-start should prevent the request from ever starting), you could recheck cancellation inside the second synchronized block before assigning state:- if (dataTask) { + if (dataTask) { if (self.options & SDWebImageDownloaderHighPriority) { dataTask.priority = NSURLSessionTaskPriorityHigh; } else if (self.options & SDWebImageDownloaderLowPriority) { dataTask.priority = NSURLSessionTaskPriorityLow; } else { dataTask.priority = NSURLSessionTaskPriorityDefault; } - NSArray<SDWebImageDownloaderOperationToken *> *tokens; - @synchronized (self) { - tokens = [self.callbackTokens copy]; - self.dataTask = dataTask; - self.executing = YES; - } + BOOL shouldStart = NO; + NSArray<SDWebImageDownloaderOperationToken *> *tokens = nil; + @synchronized (self) { + if (!self.isCancelled && !self.isFinished) { + tokens = [self.callbackTokens copy]; + self.dataTask = dataTask; + self.executing = YES; + shouldStart = YES; + } + } + if (!shouldStart) { + // Operation was cancelled/finished after task creation; do not resume. + return; + } for (SDWebImageDownloaderOperationToken *token in tokens) { ... } // Put the URLSession task resume at the end of start method to avoid race condition [self.dataTask resume]; ...This keeps the “resume at end” behavior, but guarantees you never mark the operation executing or resume a task after a concurrent cancel/reset has already run.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
SDWebImage/Core/SDWebImageDownloaderOperation.m(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Unit Test (visionOS)
- GitHub Check: Unit Test (macOS)
- GitHub Check: Unit Test (iOS)
- GitHub Check: Cocoapods Demo
- GitHub Check: Cocoapods Lint
🔇 Additional comments (1)
SDWebImage/Core/SDWebImageDownloaderOperation.m (1)
170-233:dataTaskcreation inside@synchronized(self)is a solid thread-safety improvementCreating the
NSURLSessionTaskinside the synchronized block and using a localdataTaskvariable avoids unsynchronized writes toself.dataTaskversuscancel/reset, and keeps the lock held only while touching shared state. This change looks correct and clearly improves the data‑task lifetime handling aroundstart.
New Pull Request Checklist
I have read and understood the CONTRIBUTING guide
I have read the Documentation
I have searched for a similar pull request in the project and found none
I have updated this branch with the latest master to avoid conflicts (via merge from master or rebase)
I have added the required tests to prove the fix/feature I am adding
I have updated the documentation (if necessary)
I have run the tests and they pass
I have run the lint and it passes (
pod lib lint)This merge request fixes / refers to the following issues: ...
Pull Request Description
This close #3842
Maybe there are better way to fix, but this is the simplest one in my mind
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.