Browse Source

Snapshot arrays in ImagePrefetcher.handleComplete

Mitigates a crash from iterating over sources while they are mutating.

Example crash (details omitted):
```
0   App            <redacted>  Source
1   App            <redacted>  Source
2   App            <redacted>  Array._getElement
3   App            <redacted>  Sequence.compactMap
4   App            <redacted>  ImagePrefetcher.handleComplete (ImagePrefetcher.swift:379)
```
Eric Horacek 1 month ago
parent
commit
506316b153
1 changed files with 13 additions and 6 deletions
  1. 13 6
      Sources/Networking/ImagePrefetcher.swift

+ 13 - 6
Sources/Networking/ImagePrefetcher.swift

@@ -370,14 +370,21 @@ public class ImagePrefetcher: CustomStringConvertible, @unchecked Sendable {
         if completionHandler == nil && completionSourceHandler == nil {
             return
         }
-        
+
+        // Snapshot arrays/handlers before switching threads to avoid concurrent mutation crashes.
+        let skipped = self.skippedSources
+        let failed = self.failedSources
+        let completed = self.completedSources
+        let completionSourceHandler = self.completionSourceHandler
+        let completionHandler = self.completionHandler
+
         // The completion handler should be called on the main thread
         CallbackQueue.mainCurrentOrAsync.execute {
-            self.completionSourceHandler?(self.skippedSources, self.failedSources, self.completedSources)
-            self.completionHandler?(
-                self.skippedSources.compactMap { $0.asResource },
-                self.failedSources.compactMap { $0.asResource },
-                self.completedSources.compactMap { $0.asResource }
+            completionSourceHandler?(skipped, failed, completed)
+            completionHandler?(
+                skipped.compactMap { $0.asResource },
+                failed.compactMap { $0.asResource },
+                completed.compactMap { $0.asResource }
             )
             self.completionHandler = nil
             self.progressBlock = nil