Conversation
WalkthroughAdds an iOS-specific guarded copy-on-draw path in SDImageCoderHelper for certain OS versions; updates the CI job to use build-for-testing + conditional test-without-building with platform-specific flags; increases async test timeouts, adds a VisionOS timeout and skip helper; several tests now early-skip network tests on Vision Pro CI. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant SDImageCoderHelper
participant OSCheck
participant CGImage
Caller->>SDImageCoderHelper: request draw(cgImage, context)
SDImageCoderHelper->>OSCheck: inspect OS major/minor
alt affected iOS (pre-iOS17 condition)
SDImageCoderHelper->>CGImage: create copy of cgImage
alt copy created
SDImageCoderHelper->>CGImage: draw(copiedImage, context)
SDImageCoderHelper->>CGImage: release(copiedImage)
else copy failed
SDImageCoderHelper->>CGImage: draw(original cgImage, context)
end
else newer OS / not affected
SDImageCoderHelper->>CGImage: draw(original cgImage, context)
end
SDImageCoderHelper-->>Caller: drawing complete
sequenceDiagram
participant GitHubActions
participant xcodebuild
participant Tests
GitHubActions->>xcodebuild: build-for-testing (CODE_SIGNING_ALLOWED=NO, -parallel-testing-enabled NO)
alt visionOS
GitHubActions->>xcodebuild: test-without-building (-parallel-testing-worker-count 1, -test-timeouts-enabled YES, -default-test-execution-time-allowance 30)
else other platforms
GitHubActions->>xcodebuild: test-without-building (-parallel-testing-enabled NO, -parallel-testing-worker-count 1)
end
xcodebuild-->>Tests: run tests
Note over Tests: Some tests call SDTestCase.shouldSkipNetworkTestsOnVisionPro to skip network tests on Vision Pro CI
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Potential attention points:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (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). (5)
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.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
SDWebImage/Core/SDImageCoderHelper.m(1 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: Cocoapods Lint
- GitHub Check: Unit Test (iOS)
- GitHub Check: Cocoapods Demo
🔇 Additional comments (2)
SDWebImage/Core/SDImageCoderHelper.m (2)
484-487: Verify whether tvOS 16.7 requires the same workaround.The availability check only specifies
iOS 16.7but this code runs on tvOS as well (guarded bySD_UIKITat line 26). If tvOS 16.7 has the same multithreading crash, the availability attribute should include it:@available(iOS 16.7, tvOS 16.7, *). If tvOS is unaffected, the runtime check should explicitly exclude it.Please confirm whether tvOS 16.7 exhibits the same crash and update the implementation accordingly. You can check the commit history, issue reports, or internal testing logs to determine if tvOS is affected.
487-487: Consider whether patch versions (16.7.1, 16.7.2, etc.) require the workaround.The current check
majorVersion == 16 && minorVersion == 7applies the workaround to all iOS 16.7.x patch releases. If the crash was fixed in a specific patch (e.g., 16.7.2), you could refine the check to exclude later patches:if (currentVersion.majorVersion == 16 && currentVersion.minorVersion == 7 && currentVersion.patchVersion < 2)If the issue persists across all 16.7.x versions, the current implementation is correct.
SDWebImage/Core/SDImageCoderHelper.m
Outdated
| // iOS 16.7 has CGContextDrawImage multithreading crash issue, use safe copy approach | ||
| if (@available(iOS 16.7, *)) { | ||
| // Check if it's version 16.7 (excluding versions above 16.7) | ||
| NSOperatingSystemVersion currentVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; | ||
| if (currentVersion.majorVersion == 16 && currentVersion.minorVersion == 7) { | ||
| // iOS 16.7: Use safe copy to prevent multithreading CGImage crash | ||
| CGImageRef safeImage = CGImageCreateCopy(cgImage); | ||
| if (safeImage) { | ||
| CGContextDrawImage(context, CGRectMake(0, 0, width, height), safeImage); | ||
| CGImageRelease(safeImage); | ||
| } else { | ||
| // If copy fails, fallback to original approach | ||
| CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); | ||
| } | ||
| } else { | ||
| // Other versions: Use original approach | ||
| CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); | ||
| } | ||
| } else { | ||
| // Versions below iOS 16.7: Use original approach | ||
| CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); | ||
| } |
There was a problem hiding this comment.
Add more detailed documentation and reference the underlying issue.
The comment mentions "multithreading crash issue" but lacks crucial context: which specific threading scenario triggers it, whether Apple has acknowledged it (Radar/FB number), and why copying the CGImage resolves it. Providing this information helps future maintainers understand whether the workaround is still necessary after iOS updates.
Consider expanding the comment:
- // iOS 16.7 has CGContextDrawImage multithreading crash issue, use safe copy approach
+ // Workaround: iOS 16.7.x has a crash when CGContextDrawImage is called concurrently
+ // with the same CGImageRef from multiple threads. Creating a copy ensures each thread
+ // operates on an independent CGImage instance. See issue #XXXX or rdar://XXXXXXXX.🤖 Prompt for AI Agents
In SDWebImage/Core/SDImageCoderHelper.m around lines 483 to 504, expand the
inline comment that currently says "iOS 16.7 has CGContextDrawImage
multithreading crash issue, use safe copy approach" to include: the specific
threading scenario (concurrent CGContextDrawImage calls from background threads
rendering the same CGImage), a short explanation of why CGImageCreateCopy fixes
it (creates an immutable thread-safe snapshot avoiding internal shared state),
any public reference or bug ID if available (e.g. Apple forum/radar/FB ticket
number or a GitHub issue URL), and guidance for maintainers about when it is
safe to remove the workaround (e.g. after verification on iOS >16.7 or when
Apple issues a fix). Ensure the comment remains concise and placed immediately
above the conditional block.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Tests/Tests/SDTestCase.m (1)
12-55: Critical: The iOS 16.7 crash fix file is missing from this review.The PR title states "Fix the crash issue on iOS 16.7" and the AI summary mentions "Adds an iOS 16.7-specific copy-on-draw guard in SDImageCoderHelper.m", but that file is not included in the files provided for review.
All changes in the current review are test infrastructure improvements (timeouts, CI workflow, test skipping) focused on VisionOS CI stability, not iOS 16.7 crash fixes.
Please ensure the actual crash fix in
SDImageCoderHelper.mis included in the review so the core fix can be validated. Without reviewing that file, we cannot confirm:
- The iOS 16.7 crash is actually fixed
- The fix is correct and doesn't introduce side effects
- The changes in this PR match the stated objective
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/CI.yml(1 hunks)Tests/Tests/SDTestCase.h(2 hunks)Tests/Tests/SDTestCase.m(3 hunks)Tests/Tests/SDWebImageDownloaderTests.m(1 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). (2)
- GitHub Check: Unit Test (visionOS)
- GitHub Check: Cocoapods Demo
🔇 Additional comments (6)
.github/workflows/CI.yml (1)
153-188: CI workflow improvements look solid for stability.The separation of build-for-testing and test-without-building phases is a good practice. Disabling parallel testing and limiting worker count to 1 should improve test reliability, especially for VisionOS. The platform-specific timeout configuration (30s for VisionOS) aligns well with the test infrastructure changes in SDTestCase.
However, note that these changes address CI test stability and don't directly relate to the iOS 16.7 crash mentioned in the PR title.
Tests/Tests/SDTestCase.h (2)
23-23: LGTM! Clean addition of VisionOS timeout constant.The new
kAsyncTestTimeoutVisionOSconstant is properly declared and will be used for platform-specific timeout handling.
39-39: LGTM! Test skip helper is well-designed.The
shouldSkipNetworkTestsOnVisionProclass method provides a clean way to conditionally skip network-heavy tests on Vision Pro CI environments.Tests/Tests/SDTestCase.m (2)
28-32: Platform-specific timeout selection is correctly implemented.The conditional timeout logic properly uses
kAsyncTestTimeoutVisionOSwhenSD_VISIONis defined, falling back to the base timeout otherwise.
48-55: LGTM! Clean implementation of Vision Pro test skip logic.The method correctly returns
YESonly when running on VisionOS in a CI environment, allowing local development to run all tests while avoiding known CI issues.Tests/Tests/SDWebImageDownloaderTests.m (1)
452-455: Workaround for Vision Pro CI network test instability.This guard skips the test on Vision Pro CI rather than addressing the root cause of the instability. While pragmatic for unblocking CI, consider investigating why this specific test fails on Vision Pro.
Further verification reveals that at least 7 other network-heavy tests in this file (test23, test24, test26, test27, test28, test29, test30) also make network calls but lack the same Vision Pro skip guard. This suggests either: (1) the instability is specific to test21's cancellation logic, or (2) the workaround was incompletely applied across all affected tests.
| const int64_t kAsyncTestTimeout = 15; | ||
| const int64_t kAsyncTestTimeoutVisionOS = 30; // Longer timeout for Vision Pro simulator |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Verify timeout increases are necessary and don't mask real issues.
The base timeout increased from 5 to 15 seconds (3x), and VisionOS uses 30 seconds (6x the original). While these may be necessary for CI stability, such significant increases could mask actual performance problems or hanging tests.
Please verify:
- Are there specific tests that were timing out at 5 seconds but pass reliably at 15/30 seconds?
- Have you measured actual test execution times to justify these values?
- Could underlying performance issues be addressed instead of increasing timeouts?
Consider adding a comment explaining why these specific timeout values were chosen, referencing any performance data or CI run history that justifies the increases.
🏁 Script executed:
#!/bin/bash
# Step 1: Examine the actual timeout constants in the file
echo "=== Current timeout values in SDTestCase.m ==="
cat Tests/Tests/SDTestCase.m | head -20
echo ""
echo "=== Git blame for timeout lines to see who changed them ==="
cd Tests/Tests && git blame -L 12,13 SDTestCase.m 2>/dev/null || echo "Git blame unavailable"
cd - > /dev/null
echo ""
echo "=== Recent git history for this file ==="
git log --oneline -20 Tests/Tests/SDTestCase.m 2>/dev/null || echo "Git log unavailable"
echo ""
echo "=== Search for previous timeout value mentions (5 seconds) ==="
rg -n "kAsyncTestTimeout|AsyncTestTimeout" Tests/ --type objc -C 3Length of output: 10738
🏁 Script executed:
#!/bin/bash
# Step 2: Find all usages of these timeout constants
echo "=== Usages of kAsyncTestTimeout constants in test files ==="
rg -n "kAsyncTestTimeout" Tests/ --type objc
echo ""
echo "=== Check for any explanatory comments in the file ==="
cat Tests/Tests/SDTestCase.mLength of output: 4881
🏁 Script executed:
#!/bin/bash
# Step 3: Look for CI configuration or test execution records
echo "=== Search for CI/CD configuration files ==="
fd -e yml -e yaml -e json -e xml | grep -E "(circleci|travis|github|gitlab|jenkins|test|ci)" | head -20
echo ""
echo "=== Check if there are any test performance records or documentation ==="
find . -name "*performance*" -o -name "*benchmark*" -o -name "*timeout*" | grep -v node_modules | grep -v ".git" | head -20Length of output: 118
🏁 Script executed:
#!/bin/bash
# Get the full git diff for the commit that changed timeouts
echo "=== Git show for commit eb09615 (full diff) ==="
git show eb09615 | head -100
echo ""
echo "=== Full commit message ==="
git show -s --format=%B eb09615
echo ""
echo "=== Previous version of SDTestCase.m (parent commit) ==="
git show eb09615~1:Tests/Tests/SDTestCase.m 2>/dev/null | head -20 || echo "Could not retrieve parent version"Length of output: 5579
🏁 Script executed:
#!/bin/bash
# Search for any GitHub Actions or workflow files
echo "=== Search for GitHub Actions workflows ==="
find .github -name "*.yml" -o -name "*.yaml" 2>/dev/null | xargs cat 2>/dev/null || echo "No .github directory"
echo ""
echo "=== Search for any documentation about test timeouts ==="
rg -i "timeout|performance|ci.*test" --type md --type txt -C 2 | head -50Length of output: 14894
🏁 Script executed:
#!/bin/bash
# Look at the git log for changes to timeout values across history
echo "=== Full git log showing all changes to timeout constants ==="
git log -p --follow Tests/Tests/SDTestCase.m 2>/dev/null | grep -A 5 -B 5 "kAsyncTestTimeout" | head -100Length of output: 61
Add documentation explaining timeout increases and their connection to CI changes.
The timeout increase from 5s to 15s (3x) and addition of 30s for visionOS (6x) are confirmed. While the same commit that modified these constants also disabled parallel testing and limited workers to 1 in the CI configuration (.github/workflows/CI.yml), this architectural context is not documented in the code itself.
Add a comment explaining the rationale. For example:
// Increased from 5s due to visionOS simulator performance characteristics
// and disabling parallel test execution (see CI.yml). Values derived from
// test stability requirements in CI environment.
const int64_t kAsyncTestTimeout = 15;
const int64_t kAsyncTestTimeoutVisionOS = 30; // Vision Pro simulator requires longer allowanceThis clarifies that the increases address CI stability (parallel testing disabled) rather than purely network issues, helping future maintainers understand whether these values can be adjusted.
🤖 Prompt for AI Agents
In Tests/Tests/SDTestCase.m around lines 12-13, there is no comment explaining
why kAsyncTestTimeout was raised from 5s to 15s and why
kAsyncTestTimeoutVisionOS was added as 30s; add a brief comment above these
constants stating that the values were increased to improve CI test stability
after parallel test execution was disabled and workers were limited to 1 in
.github/workflows/CI.yml, note the multiplier (3x and 6x) and that the values
reflect observed simulator/CI timing characteristics so future maintainers know
the rationale and where to look in CI config for context.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
Tests/Tests/SDAnimatedImageTest.m (1)
740-743: Consider refactoring duplicated skip guards.The same Vision Pro skip guard pattern appears in three consecutive tests (test33, test34, test35). While this duplication is minimal, you could reduce it by:
- Moving the guard to a shared helper method, or
- Adding the skip logic in the
setUpmethod with a flag to conditionally skip specific tests.Example helper approach:
+- (BOOL)skipIfVisionProCI:(NSString *)testName { + if ([SDTestCase shouldSkipNetworkTestsOnVisionPro]) { + NSLog(@"Skipping %@ on Vision Pro in CI", testName); + return YES; + } + return NO; +} + - (void)test33AnimatedImagePlaybackModeReverse { - if ([SDTestCase shouldSkipNetworkTestsOnVisionPro]) { - NSLog(@"Skipping test33AnimatedImagePlaybackModeReverse on Vision Pro in CI"); + if ([self skipIfVisionProCI:@"test33AnimatedImagePlaybackModeReverse"]) { return; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Tests/Tests/SDAnimatedImageTest.m(3 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: Cocoapods Demo
- GitHub Check: Build Library
- GitHub Check: Cocoapods Lint
- GitHub Check: Unit Test (iOS)
- GitHub Check: Unit Test (visionOS)
| if ([SDTestCase shouldSkipNetworkTestsOnVisionPro]) { | ||
| NSLog(@"Skipping test33AnimatedImagePlaybackModeReverse on Vision Pro in CI"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Method name doesn't match test type.
The method shouldSkipNetworkTestsOnVisionPro suggests it's for network tests, but test33AnimatedImagePlaybackModeReverse loads local test data (testAPNGPData) and doesn't perform network operations. This naming inconsistency could cause confusion about the skip guard's purpose.
Consider either:
- Renaming the method to something more generic like
shouldSkipAnimationPlaybackTestsOnVisionPro, or - Using a different, appropriately-named method if these tests have specific Vision Pro compatibility issues.
🤖 Prompt for AI Agents
In Tests/Tests/SDAnimatedImageTest.m around lines 639 to 642, the guard uses
shouldSkipNetworkTestsOnVisionPro which is misleading because
test33AnimatedImagePlaybackModeReverse uses local testAPNGPData; rename the
predicate to a generic name like shouldSkipAnimationPlaybackTestsOnVisionPro (or
call a more specific Vision Pro compatibility method) and update its
implementation and all call sites accordingly so the method name matches its
purpose and other tests remain consistent.
|
这个不仅仅在iOS16上出现 @dreampiggy |
|
This is not thread safe. It's OOM (see that top stack You can pass |

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
...
Summary by CodeRabbit
Bug Fixes
Tests
Chores