Skip to content

Commit 54ee842

Browse files
committed
Fix synchronization issue in DataLoader
1 parent b28a9b4 commit 54ee842

File tree

1 file changed

+32
-18
lines changed

1 file changed

+32
-18
lines changed

Sources/Get/DataLoader.swift

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import FoundationNetworking
99

1010
// A simple URLSession wrapper adding async/await APIs compatible with older platforms.
1111
final class DataLoader: NSObject, URLSessionDataDelegate, URLSessionDownloadDelegate, @unchecked Sendable {
12-
private var handlers = [URLSessionTask: TaskHandler]()
12+
private let handlers = TaskHandlersDictionary()
1313

1414
var userSessionDelegate: URLSessionDelegate? {
1515
didSet {
@@ -31,11 +31,10 @@ final class DataLoader: NSObject, URLSessionDataDelegate, URLSessionDownloadDele
3131
func startDataTask(_ task: URLSessionDataTask, session: URLSession, delegate: URLSessionDataDelegate?) async throws -> Response<Data> {
3232
try await withTaskCancellationHandler(handler: { task.cancel() }) {
3333
try await withUnsafeThrowingContinuation { continuation in
34-
session.delegateQueue.addOperation {
35-
let handler = DataTaskHandler(delegate: delegate)
36-
handler.completion = continuation.resume(with:)
37-
self.handlers[task] = handler
38-
}
34+
let handler = DataTaskHandler(delegate: delegate)
35+
handler.completion = continuation.resume(with:)
36+
self.handlers[task] = handler
37+
3938
task.resume()
4039
}
4140
}
@@ -44,11 +43,10 @@ final class DataLoader: NSObject, URLSessionDataDelegate, URLSessionDownloadDele
4443
func startDownloadTask(_ task: URLSessionDownloadTask, session: URLSession, delegate: URLSessionDownloadDelegate?) async throws -> Response<URL> {
4544
try await withTaskCancellationHandler(handler: { task.cancel() }) {
4645
try await withUnsafeThrowingContinuation { continuation in
47-
session.delegateQueue.addOperation {
48-
let handler = DownloadTaskHandler(delegate: delegate)
49-
handler.completion = continuation.resume(with:)
50-
self.handlers[task] = handler
51-
}
46+
let handler = DownloadTaskHandler(delegate: delegate)
47+
handler.completion = continuation.resume(with:)
48+
self.handlers[task] = handler
49+
5250
task.resume()
5351
}
5452
}
@@ -57,11 +55,10 @@ final class DataLoader: NSObject, URLSessionDataDelegate, URLSessionDownloadDele
5755
func startUploadTask(_ task: URLSessionUploadTask, session: URLSession, delegate: URLSessionTaskDelegate?) async throws -> Response<Data> {
5856
try await withTaskCancellationHandler(handler: { task.cancel() }) {
5957
try await withUnsafeThrowingContinuation { continuation in
60-
session.delegateQueue.addOperation {
61-
let handler = DataTaskHandler(delegate: delegate)
62-
handler.completion = continuation.resume(with:)
63-
self.handlers[task] = handler
64-
}
58+
let handler = DataTaskHandler(delegate: delegate)
59+
handler.completion = continuation.resume(with:)
60+
self.handlers[task] = handler
61+
6562
task.resume()
6663
}
6764
}
@@ -366,6 +363,23 @@ func decode<T: Decodable>(_ data: Data, using decoder: JSONDecoder) async throws
366363
}
367364
}
368365

366+
/// With iOS 16, there is now a delegate method (`didCreateTask`) that gets
367+
/// called outside of the session's delegate queue, which means that the access
368+
/// needs to be synchronized.
369+
private final class TaskHandlersDictionary {
370+
private let lock = NSLock()
371+
private var handlers = [URLSessionTask: TaskHandler]()
369372

370-
371-
373+
subscript(task: URLSessionTask) -> TaskHandler? {
374+
get {
375+
lock.lock()
376+
defer { lock.unlock() }
377+
return handlers[task]
378+
}
379+
set {
380+
lock.lock()
381+
defer { lock.unlock() }
382+
handlers[task] = newValue
383+
}
384+
}
385+
}

0 commit comments

Comments
 (0)