-
Notifications
You must be signed in to change notification settings - Fork 167
Release media resource loader if not being used #3860
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
base: release/8.3
Are you sure you want to change the base?
Conversation
|
@claude can you review this PR |
|
Claude finished @SergioEstevao's task in 2m 9s —— View job Code Review Complete ✅I've completed a comprehensive review of this PR that addresses the SummaryThe PR correctly identifies and fixes a retain cycle between ✅ Strengths
🔍 Key Issues FoundI've left inline comments on specific code locations. Here are the main concerns: 1. Thread Safety Issue with
|
| var error: Error? | ||
| } | ||
|
|
||
| private var activeLoaderDelegate: AVAssetResourceLoaderDelegate? |
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.
Thread Safety Concern: This property is accessed from multiple threads without synchronization:
- Written on lines 355, 413 (potentially from background queues)
- Read/compared on line 423 (from async Task)
Consider making this thread-safe using a lock or making DownloadManager an actor.
private let activeLoaderLock = NSLock()
private var _activeLoaderDelegate: AVAssetResourceLoaderDelegate?
private var activeLoaderDelegate: AVAssetResourceLoaderDelegate? {
get {
activeLoaderLock.lock()
defer { activeLoaderLock.unlock() }
return _activeLoaderDelegate
}
set {
activeLoaderLock.lock()
_activeLoaderDelegate = newValue
activeLoaderLock.unlock()
}
}| if FeatureFlag.releaseMediaExporterWhenNoLongerActive.enabled { | ||
| activeLoaderDelegate = customDelegate | ||
| } |
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.
Potential Issue: When reusing an existing loader, we don't check if there's a different active loader that should be released first.
Scenario:
- Episode A playing (loader A active)
- Start episode B (loader B active, A marked to release)
- Resume episode A (loader A becomes active again)
At step 3, loader A might still be marked for release from step 2. Consider clearing the release flag or checking loader state before reuse.
| if FeatureFlag.releaseMediaExporterWhenNoLongerActive.enabled, | ||
| let mediaExporterDelegate = downloadAndStreamEpisodes[downloadTaskUUID] as? MediaExporterResourceLoaderDelegate, | ||
| mediaExporterDelegate != activeLoaderDelegate as? MediaExporterResourceLoaderDelegate { | ||
| mediaExporterDelegate.markToRelease() | ||
| } |
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.
Cleanup Question: What happens to activeLoaderDelegate if this loader is the active one and it just completed with an error? Should we clear activeLoaderDelegate = nil in that case?
Also, consider simplifying the nested feature flag checks by extracting to a helper method or checking once at the start.
| func markToRelease() { | ||
| if isDownloadComplete { | ||
| invalidateAndCancelSession(shouldResetData: false) | ||
| } | ||
| } |
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.
Naming suggestion: The method name markToRelease() suggests it unconditionally marks for release, but it only releases if isDownloadComplete. Consider renaming to releaseIfComplete() or adding documentation to clarify the conditional behavior.
| 📘 Part of: # |
|:---:|
Fixes PCIOS-
While debugging the media resource loader file handle I found out that our custom resource loader was never been released.
After some investigation I found that the reason is that the our loader and the session used to download the media file where in a strong reference cycle chain.
This happening because the
loaderis the delegate for the session and the session holds a strong reference for it.MediaExportResourceLoader <-> URLSession
My initial thought was to break this cycle by invalidating the session when the download was complete.
When I tried that I found out that breaking that reference chain when the download completed made the loader to be free while it was still being used by the AVURLAsset while in play. This is because the AVAssetResourceLoader does not retain the custom delegate we provided to it.
So if release the session reference, by invalidating the session at the end of the download we end up releasing the loader, and the asset stop responding for data requests.
So I looked at an alternative method, and end up implementing on the DownloadManager a reference to the last provided Resource Loader Delegate. This way when the DownloadManager starts new one, we can say to the old to be released when/if it completed the download.
In order to be safe I implemented this code around a FF
To test
MediaExporterResourceLoaderDelegate: Start data request for XXXMediaExporterResourceLoaderDelegate: Releasing loader forand another message of the type:MediaExporterResourceLoaderDelegate: Start data request for XXXreleaseMediaExporterWhenNoLongerActivedisabled to see if all works correctlyChecklist
CHANGELOG.mdif necessary.