Procházet zdrojové kódy

Avoid racing for accessing originalRequest

This patch adds/adjusts some lock behaviors. This should help to avoid racing for accessing originalRequest properties and fix #1511
onevcat před 5 roky
rodič
revize
346c45ac64

+ 1 - 1
Sources/Networking/ImageDownloader.swift

@@ -186,7 +186,7 @@ open class ImageDownloader {
             }
         }
         sessionDelegate.onDidDownloadData.delegate(on: self) { (self, task) in
-            guard let url = task.task.originalRequest?.url else {
+            guard let url = task.originalURL else {
                 return task.mutableData
             }
             return (self.delegate ?? self).imageDownloader(self, didDownload: task.mutableData, for: url)

+ 5 - 0
Sources/Networking/SessionDataTask.swift

@@ -41,6 +41,10 @@ public class SessionDataTask {
     /// Downloaded raw data of current task.
     public private(set) var mutableData: Data
 
+    // This is a copy of `task.originalRequest?.url`. It is for getting a race-safe behavior for a pitfall on iOS 13.
+    // Ref: https://github.com/onevcat/Kingfisher/issues/1511
+    let originalURL: URL?
+
     /// The underlying download task. It is only for debugging purpose when you encountered an error. You should not
     /// modify the content of this task or start it yourself.
     public let task: URLSessionDataTask
@@ -70,6 +74,7 @@ public class SessionDataTask {
 
     init(task: URLSessionDataTask) {
         self.task = task
+        self.originalURL = task.originalRequest?.url
         mutableData = Data()
     }
 

+ 13 - 6
Sources/Networking/SessionDelegate.swift

@@ -73,7 +73,8 @@ class SessionDelegate: NSObject {
             // No other callbacks waiting, we can clear the task now.
             if !task.containsCallbacks {
                 let dataTask = task.task
-                dataTask.cancel()
+
+                self.cancelTask(dataTask)
                 self.remove(dataTask)
             }
         }
@@ -82,6 +83,12 @@ class SessionDelegate: NSObject {
         return DownloadTask(sessionTask: task, cancelToken: token)
     }
 
+    private func cancelTask(_ dataTask: URLSessionDataTask) {
+        lock.lock()
+        defer { lock.unlock() }
+        dataTask.cancel()
+    }
+
     func append(
         _ task: SessionDataTask,
         url: URL,
@@ -92,22 +99,22 @@ class SessionDelegate: NSObject {
     }
 
     private func remove(_ task: URLSessionTask) {
+        lock.lock()
+        defer { lock.unlock() }
+
         guard let url = task.originalRequest?.url else {
             return
         }
-        lock.lock()
-        defer {lock.unlock()}
         tasks[url] = nil
     }
 
     private func task(for task: URLSessionTask) -> SessionDataTask? {
+        lock.lock()
+        defer { lock.unlock() }
 
         guard let url = task.originalRequest?.url else {
             return nil
         }
-
-        lock.lock()
-        defer { lock.unlock() }
         guard let sessionTask = tasks[url] else {
             return nil
         }