Repository navigation
Fix for NSInvalidArgumentException in PINProgressiveImage.m:268 - #657
Merged
andyfinnell merged 4 commits intoNov 6, 2026
Merged
andyfinnell merged 4 commits into
andyfinnell merged 4 commits into
Conversation
Try fix by removing the doubling memory usage caused by [mutableData copy] which in cases of memory pressure would fault with NSInvalidArgumentException.
Add +[PINRemoteImageManager memorySafeProgressiveDataEnabled] (default NO) so consumers can roll out the PINProgressiveImage OOM fix behind an experiment. When enabled, the download completion path uses -hasData/-takeData instead of copying the buffer, and -[PINProgressiveImage data] catches allocation exceptions. When disabled, both paths behave exactly as before. The PINRemoteLock @try/@finally fix stays unconditional. Co-authored-by: Cursor <cursoragent@cursor.com>
# Short title (50 chars max) # More detailed description if needed. Wrap around 72 chars. # # - Use bullet points if helpful # # Subject line (50 chars) - Hook will add [TICKET-123] automatically # # Body: Explain WHAT changed and WHY (not HOW) # - Use bullet points if needed # - Wrap at ~72 characters # # Reference any related issues at the bottom: # https://jira.pinadmin.com/browse/@TICKET
Contributor
Author
|
Regarding the Loremflickr failure
The site has this statement https://loremflickr.com/
|
andyfinnell
requested changes
Nov 6, 2026
andyfinnell
left a comment
Contributor
There was a problem hiding this comment.
The only blocking change is to fix the currently failing test.
The rest are suggestions.
Contributor
Author
Fixed the test changing to https://tools.placeholder.com/images |
vnavarro
force-pushed
the
vnavarro/BUG-PINProgressiveImageCrash
branch
from
November 6, 2026 15:14
b569622 to
338f1c0
Compare
andyfinnell
approved these changes
Nov 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes an
NSInvalidArgumentExceptioncrash in-[PINProgressiveImage data]under memory pressure, with an opt-in, per-manager switch onPINRemoteImageManagerConfiguration.Why
-datadoes[self.mutableData copy], which doubles peak memory at the worst moment (the end of a large download). Under memory pressure, Foundation's page allocator raisesNSInvalidArgumentExceptionfrom-copyinstead of returning nil, and nothing caught it.Changes
Crash fix
PINRemoteLock.lockWithBlock:now unlocks in@finally. Without this, an exception escaping any of the ~40lockWithBlock:call sites leaves the mutex permanently held (confirmed:pthread_mutex_trylockreturnsEBUSYafter the escape). This is a correctness fix independent of the OOM crash.-[PINProgressiveImage data]wraps the copy in@try/@catchand returns nil on failure.-datais already nullable and every caller already tolerates nil.-takeData(inPINProgressiveImage+Private.h, not the public header) transfers ownership of the buffer instead of copying it. The completion path inPINRemoteImageDownloadTaskno longer allocates a second buffer, which removes the doubling instead of just surviving it. AdataTakenflag stops a late in-flight append from corrupting a downstream incremental decode.-hasDatalets the retry/emptiness check avoid materializing the buffer. On a retry the data is discarded anyway, and the allocation itself can fail.Opt-in switch (per manager, via configuration)
PINRemoteImageManagerConfiguration.memorySafeProgressiveDataEnabled(getterisMemorySafeProgressiveDataEnabled), defaultNO.PINRemoteImageManagerclass property, which is removed from the public header. Each manager copies the value from its configuration at init.PINRemoteImageDownloadTaskreads it from its manager and forwards it to the progress image through the newPINProgressiveImage.memorySafeDataEnabledproperty.-databehave exactly as before.-hasDataand-takeDatainstead of copying, and-datareturns nil instead of raising when the copy can't be allocated.PINRemoteLock@finallyfix is always on, because it only changes behavior once an exception has already escaped.Note:
+sharedImageManageruses the default configuration, so it can't enable the switch. Callers who want it on must create their own manager with a configuration.Precedent:
PINDiskCache.min PINCache already wraps its deserializer in@try/@catch (NSException *)for the same class of problem.Test Plan
PINProgressiveImageTests. It covershasData/takeDatasemantics, thedataTakenguard, and a dynamic subclass that forces-copyto throw, to prove-datareturns nil when memory-safe is on and still throws when it is off.PINRemoteLockTests. It proves the lock can be reacquired from another thread after a block throws.xcodebuild test -workspace PINRemoteImage.xcworkspace -scheme PINRemoteImage -destination <iOS simulator>.PINRemoteImage_Tests(existing)PINProgressiveImageTests(new)PINRemoteLockTests(new)