-
-
Notifications
You must be signed in to change notification settings - Fork 6k
Using @synchronized to always lock the cache/loader operation to ensure thread-safe. Fix issue on iOS 26 #3855
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
Using @synchronized to always lock the cache/loader operation to ensure thread-safe. Fix issue on iOS 26 #3855
Conversation
…re thread-safe Fix issue on iOS 26
WalkthroughThread-safety improvements to SDWebImageManager by wrapping assignments to Changes
Sequence DiagramsequenceDiagram
participant Main as Main Thread
participant Cache as Cache Queue
participant Download as Download Queue
rect rgb(200, 220, 255)
Note over Main, Download: BEFORE: Unsafe concurrent assignment
Cache->>Main: queryImageForKey() returns
Download->>Main: download completes
Note over Main: Race condition:<br/>Both try to set operation.cacheOperation<br/>operation.loaderOperation
Main->>Main: ❌ Garbage pointer dereference
end
rect rgb(200, 255, 220)
Note over Main, Download: AFTER: Synchronized assignment
Cache->>Main: queryImageForKey() returns
Main->>Main: ⬇️ Create local variable
Main->>Main: `@synchronized` (operation) { assign }
Download->>Main: download completes
Main->>Main: ⬇️ Create local variable
Main->>Main: `@synchronized` (operation) { assign }
Note over Main: ✓ Atomic updates prevent corruption
end
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~10 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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 |
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 (2)
SDWebImage/Core/SDWebImageManager.m (2)
311-333: Good alignment ofcacheOperationlocking withcancel’s monitorWrapping the
operation.cacheOperationassignments in@synchronized(operation)is a solid fix: it uses the same monitor object as@synchronized(self)inSDWebImageCombinedOperation’s-isCancelledand-cancel, which removes races between the cancel path and manager-side cache-operation mutations while keeping the potentially heavyqueryImageForKey:calls outside the lock.One optional improvement you might consider (for both normal and original cache paths) is to immediately cancel a newly-assigned cache operation if the combined operation was already cancelled before this assignment ran, to avoid doing unnecessary work after user cancellation:
- @synchronized (operation) { - operation.cacheOperation = cacheOperation; - } + @synchronized (operation) { + operation.cacheOperation = cacheOperation; + if (operation.isCancelled) { + [cacheOperation cancel]; + operation.cacheOperation = nil; + } + }Same pattern would apply in
callOriginalCacheProcessForOperation:after the othercacheOperationassignment.Also applies to: 368-388, 783-807
442-477:loaderOperationsynchronization is consistent; consider mirroring the post-cancel guardThe new
@synchronized(operation)aroundoperation.loaderOperation = loaderOperation;correctly brings the download sub-operation under the same monitor used bySDWebImageCombinedOperation’s-cancel, so cancel and assignment can’t race anymore.For symmetry with the cache side and to avoid starting network work for already-cancelled combined operations, you could optionally add a post-assignment check here:
- @synchronized (operation) { - operation.loaderOperation = loaderOperation; - } + @synchronized (operation) { + operation.loaderOperation = loaderOperation; + if (operation.isCancelled) { + [loaderOperation cancel]; + operation.loaderOperation = nil; + } + }This doesn’t affect correctness of the current fix, but tightens cancel semantics and resource usage.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
SDWebImage/Core/SDWebImageManager.m(6 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 (macOS)
- GitHub Check: Unit Test (visionOS)
- GitHub Check: Unit Test (iOS)
- GitHub Check: Cocoapods Lint
- GitHub Check: Cocoapods Demo
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 #3849
Actually I think this is inddeed thread-unsafe. But I have no idea why pre iOS-26 does not have such a issue...
Anyway, use
@synchronizedcan be safe guraded in-[SDWebImageCombinedOperation cancel]method, which use@synchronized (self)Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.