Skip to content

Commit d2212fe

Browse files
author
Pushkar N Kulkarni
authored
Merge pull request #662 from aleksgapp/finish-tasks-deadlock
[SR-2798] Fix deadlock in URLSession.
2 parents b0d764d + d6a74b0 commit d2212fe

File tree

3 files changed

+43
-16
lines changed

3 files changed

+43
-16
lines changed

Foundation/NSURLSession/NSURLSession.swift

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,20 @@ open class URLSession : NSObject {
265265
//don't allow creation of new tasks from this point onwards
266266
self.invalidated = true
267267

268-
//wait for running tasks to finish
269-
if !self.taskRegistry.isEmpty {
270-
let tasksCompletion = DispatchSemaphore(value: 0)
271-
self.taskRegistry.notify(on: tasksCompletion)
272-
tasksCompletion.wait()
268+
let invalidateSessionCallback = { [weak self] in
269+
//invoke the delegate method and break the delegate link
270+
guard let `self` = self, let sessionDelegate = self.delegate else { return }
271+
self.delegateQueue.addOperation {
272+
sessionDelegate.urlSession(self, didBecomeInvalidWithError: nil)
273+
self.delegate = nil
274+
}
273275
}
274276

275-
//invoke the delegate method and break the delegate link
276-
guard let sessionDelegate = self.delegate else { return }
277-
self.delegateQueue.addOperation {
278-
sessionDelegate.urlSession(self, didBecomeInvalidWithError: nil)
279-
self.delegate = nil
277+
//wait for running tasks to finish
278+
if !self.taskRegistry.isEmpty {
279+
self.taskRegistry.notify(on: invalidateSessionCallback)
280+
} else {
281+
invalidateSessionCallback()
280282
}
281283
}
282284
}

Foundation/NSURLSession/TaskRegistry.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extension URLSession {
4545

4646
fileprivate var tasks: [Int: URLSessionTask] = [:]
4747
fileprivate var behaviours: [Int: _Behaviour] = [:]
48-
fileprivate var tasksFinished: DispatchSemaphore?
48+
fileprivate var tasksFinishedCallback: (() -> ())?
4949
}
5050
}
5151

@@ -81,14 +81,14 @@ extension URLSession._TaskRegistry {
8181
}
8282
behaviours.remove(at: behaviourIdx)
8383

84-
guard let allTasksFinished = tasksFinished else { return }
84+
guard let allTasksFinished = tasksFinishedCallback else { return }
8585
if self.isEmpty {
86-
allTasksFinished.signal()
86+
allTasksFinished()
8787
}
8888
}
8989

90-
func notify(on semaphore: DispatchSemaphore) {
91-
tasksFinished = semaphore
90+
func notify(on tasksCompetion: @escaping () -> ()) {
91+
tasksFinishedCallback = tasksCompetion
9292
}
9393

9494
var isEmpty: Bool {

TestFoundation/TestNSURLSession.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class TestURLSession : XCTestCase {
2929
("test_downloadTaskWithURLRequest", test_downloadTaskWithURLRequest),
3030
("test_downloadTaskWithRequestAndHandler", test_downloadTaskWithRequestAndHandler),
3131
("test_downloadTaskWithURLAndHandler", test_downloadTaskWithURLAndHandler),
32-
32+
("test_finishTaskAndInvalidate", test_finishTasksAndInvalidate)
3333
]
3434
}
3535

@@ -247,6 +247,31 @@ class TestURLSession : XCTestCase {
247247
task.resume()
248248
waitForExpectations(timeout: 12)
249249
}
250+
251+
func test_finishTasksAndInvalidate() {
252+
let invalidateExpectation = expectation(description: "URLSession wasn't invalidated")
253+
let delegate = SessionDelegate(invalidateExpectation: invalidateExpectation)
254+
let url = URL(string: "http://127.0.0.1:\(serverPort)/Nepal")!
255+
let session = URLSession(configuration: URLSessionConfiguration.default,
256+
delegate: delegate, delegateQueue: nil)
257+
let completionExpectation = expectation(description: "dataTask completion block wasn't called")
258+
let task = session.dataTask(with: url) { _ in
259+
completionExpectation.fulfill()
260+
}
261+
task.resume()
262+
session.finishTasksAndInvalidate()
263+
waitForExpectations(timeout: 12)
264+
}
265+
}
266+
267+
class SessionDelegate: NSObject, URLSessionDelegate {
268+
let invalidateExpectation: XCTestExpectation
269+
init(invalidateExpectation: XCTestExpectation){
270+
self.invalidateExpectation = invalidateExpectation
271+
}
272+
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
273+
invalidateExpectation.fulfill()
274+
}
250275
}
251276

252277
class DataTask : NSObject {

0 commit comments

Comments
 (0)