Skip to content

Commit ce38016

Browse files
weissiPushkar Kulkarni
authored andcommitted
cherry-pick swiftlang#1186 and fix conflicts
1 parent e37a2e6 commit ce38016

File tree

4 files changed

+124
-69
lines changed

4 files changed

+124
-69
lines changed

Foundation/URLSession/URLSession.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,13 @@ import CoreFoundation
172172
import Dispatch
173173

174174

175+
fileprivate let globalVarSyncQ = DispatchQueue(label: "org.swift.Foundation.URLSession.GlobalVarSyncQ")
175176
fileprivate var sessionCounter = Int32(0)
176177
fileprivate func nextSessionIdentifier() -> Int32 {
177-
//TODO: find an alternative for OSAtomicIncrement32Barrier() on Linux
178-
sessionCounter += 1
179-
return sessionCounter
178+
return globalVarSyncQ.sync {
179+
sessionCounter += 1
180+
return sessionCounter
181+
}
180182
}
181183
public let NSURLSessionTransferSizeUnknown: Int64 = -1
182184

Foundation/URLSession/URLSessionTask.swift

Lines changed: 26 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,11 @@ open class URLSessionTask : NSObject, NSCopying {
2929
internal var suspendCount = 1
3030
internal var session: URLSessionProtocol! //change to nil when task completes
3131
internal let body: _Body
32-
fileprivate var _protocol: URLProtocol? = nil
33-
32+
fileprivate var _protocol: URLProtocol! = nil
33+
private let syncQ = DispatchQueue(label: "org.swift.URLSessionTask.SyncQ")
34+
3435
/// All operations must run on this queue.
3536
internal let workQueue: DispatchQueue
36-
/// Using dispatch semaphore to make public attributes thread safe.
37-
/// A semaphore is a simpler option against the usage of concurrent queue
38-
/// as the critical sections are very short.
39-
fileprivate let semaphore = DispatchSemaphore(value: 1)
4037

4138
public override init() {
4239
// Darwin Foundation oddly allows calling this initializer, even though
@@ -62,7 +59,8 @@ open class URLSessionTask : NSObject, NSCopying {
6259
}
6360
internal init(session: URLSession, request: URLRequest, taskIdentifier: Int, body: _Body) {
6461
self.session = session
65-
self.workQueue = session.workQueue
62+
/* make sure we're actually having a serial queue as it's used for synchronization */
63+
self.workQueue = DispatchQueue.init(label: "org.swift.URLSessionTask.WorkQueue", target: session.workQueue)
6664
self.taskIdentifier = taskIdentifier
6765
self.originalRequest = request
6866
self.body = body
@@ -108,31 +106,19 @@ open class URLSessionTask : NSObject, NSCopying {
108106
/// May differ from originalRequest due to http server redirection
109107
/*@NSCopying*/ open internal(set) var currentRequest: URLRequest? {
110108
get {
111-
semaphore.wait()
112-
defer {
113-
semaphore.signal()
114-
}
115-
return self._currentRequest
109+
return self.syncQ.sync { return self._currentRequest }
116110
}
117111
set {
118-
semaphore.wait()
119-
self._currentRequest = newValue
120-
semaphore.signal()
112+
self.syncQ.sync { self._currentRequest = newValue }
121113
}
122114
}
123115
fileprivate var _currentRequest: URLRequest? = nil
124116
/*@NSCopying*/ open internal(set) var response: URLResponse? {
125117
get {
126-
semaphore.wait()
127-
defer {
128-
semaphore.signal()
129-
}
130-
return self._response
118+
return self.syncQ.sync { return self._response }
131119
}
132120
set {
133-
semaphore.wait()
134-
self._response = newValue
135-
semaphore.signal()
121+
self.syncQ.sync { self._response = newValue }
136122
}
137123
}
138124
fileprivate var _response: URLResponse? = nil
@@ -145,33 +131,21 @@ open class URLSessionTask : NSObject, NSCopying {
145131
/// Number of body bytes already received
146132
open internal(set) var countOfBytesReceived: Int64 {
147133
get {
148-
semaphore.wait()
149-
defer {
150-
semaphore.signal()
151-
}
152-
return self._countOfBytesReceived
134+
return self.syncQ.sync { return self._countOfBytesReceived }
153135
}
154136
set {
155-
semaphore.wait()
156-
self._countOfBytesReceived = newValue
157-
semaphore.signal()
137+
self.syncQ.sync { self._countOfBytesReceived = newValue }
158138
}
159139
}
160140
fileprivate var _countOfBytesReceived: Int64 = 0
161141

162142
/// Number of body bytes already sent */
163143
open internal(set) var countOfBytesSent: Int64 {
164144
get {
165-
semaphore.wait()
166-
defer {
167-
semaphore.signal()
168-
}
169-
return self._countOfBytesSent
145+
return self.syncQ.sync { return self._countOfBytesSent }
170146
}
171147
set {
172-
semaphore.wait()
173-
self._countOfBytesSent = newValue
174-
semaphore.signal()
148+
self.syncQ.sync { self._countOfBytesSent = newValue }
175149
}
176150
}
177151

@@ -211,16 +185,10 @@ open class URLSessionTask : NSObject, NSCopying {
211185
*/
212186
open var state: URLSessionTask.State {
213187
get {
214-
semaphore.wait()
215-
defer {
216-
semaphore.signal()
217-
}
218-
return self._state
188+
return self.syncQ.sync { self._state }
219189
}
220190
set {
221-
semaphore.wait()
222-
self._state = newValue
223-
semaphore.signal()
191+
self.syncQ.sync { self._state = newValue }
224192
}
225193
}
226194
fileprivate var _state: URLSessionTask.State = .suspended
@@ -315,16 +283,10 @@ open class URLSessionTask : NSObject, NSCopying {
315283
/// URLSessionTask.highPriority, but use is not restricted to these.
316284
open var priority: Float {
317285
get {
318-
semaphore.wait()
319-
defer {
320-
semaphore.signal()
321-
}
322-
return self._priority
286+
return self.workQueue.sync { return self._priority }
323287
}
324288
set {
325-
semaphore.wait()
326-
self._priority = newValue
327-
semaphore.signal()
289+
self.workQueue.sync { self._priority = newValue }
328290
}
329291
}
330292
fileprivate var _priority: Float = URLSessionTask.defaultPriority
@@ -569,7 +531,9 @@ extension _ProtocolClient : URLProtocolClient {
569531
session.delegateQueue.addOperation {
570532
delegate.urlSession(session, task: task, didCompleteWithError: nil)
571533
task.state = .completed
572-
session.taskRegistry.remove(task)
534+
task.workQueue.async {
535+
session.taskRegistry.remove(task)
536+
}
573537
}
574538
case .noDelegate:
575539
task.state = .completed
@@ -625,7 +589,9 @@ extension _ProtocolClient : URLProtocolClient {
625589
session.delegateQueue.addOperation {
626590
delegate.urlSession(session, task: task, didCompleteWithError: error as Error)
627591
task.state = .completed
628-
session.taskRegistry.remove(task)
592+
task.workQueue.async {
593+
session.taskRegistry.remove(task)
594+
}
629595
}
630596
case .noDelegate:
631597
task.state = .completed
@@ -634,7 +600,9 @@ extension _ProtocolClient : URLProtocolClient {
634600
session.delegateQueue.addOperation {
635601
completion(nil, nil, error)
636602
task.state = .completed
637-
session.taskRegistry.remove(task)
603+
task.workQueue.async {
604+
session.taskRegistry.remove(task)
605+
}
638606
}
639607
case .downloadCompletionHandler(let completion):
640608
session.delegateQueue.addOperation {

TestFoundation/HTTPServer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import Dispatch
2626
#endif
2727

2828
public let globalDispatchQueue = DispatchQueue.global()
29+
public let dispatchQueueMake: (String) -> DispatchQueue = { DispatchQueue.init(label: $0) }
30+
public let dispatchGroupMake: () -> DispatchGroup = DispatchGroup.init
2931

3032
struct _HTTPUtils {
3133
static let CRLF = "\r\n"

TestFoundation/TestURLSession.swift

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class TestURLSession : LoopbackServerTest {
4343
("test_illegalHTTPServerResponses", test_illegalHTTPServerResponses),
4444
("test_dataTaskWithSharedDelegate", test_dataTaskWithSharedDelegate),
4545
("test_simpleUploadWithDelegate", test_simpleUploadWithDelegate),
46+
("test_concurrentRequests", test_concurrentRequests),
4647
]
4748
}
4849

@@ -459,6 +460,34 @@ class TestURLSession : LoopbackServerTest {
459460
task.resume()
460461
waitForExpectations(timeout: 20)
461462
}
463+
464+
func test_concurrentRequests() {
465+
let syncQ = dispatchQueueMake("test_dataTaskWithURL.syncQ")
466+
var dataTasks: [DataTask] = []
467+
let g = dispatchGroupMake()
468+
for f in 0..<640 {
469+
g.enter()
470+
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
471+
let expectation = self.expectation(description: "GET \(urlString) [\(f)]: with a delegate")
472+
globalDispatchQueue.async {
473+
let url = URL(string: urlString)!
474+
let d = DataTask(with: expectation)
475+
d.run(with: url)
476+
syncQ.async {
477+
dataTasks.append(d)
478+
g.leave()
479+
}
480+
}
481+
}
482+
waitForExpectations(timeout: 12)
483+
g.wait()
484+
for d in syncQ.sync(execute: {dataTasks}) {
485+
if !d.error {
486+
XCTAssertEqual(d.capital, "Kathmandu", "test_dataTaskWithURLRequest returned an unexpected result")
487+
}
488+
}
489+
}
490+
462491
}
463492

464493
class SharedDelegate: NSObject {
@@ -488,19 +517,73 @@ class SessionDelegate: NSObject, URLSessionDelegate {
488517
}
489518

490519
class DataTask : NSObject {
520+
let syncQ = dispatchQueueMake("org.swift.TestFoundation.TestURLSession.DataTask.syncQ")
491521
let dataTaskExpectation: XCTestExpectation!
492-
var capital = "unknown"
493-
var session: URLSession! = nil
494-
var task: URLSessionDataTask! = nil
495-
var cancelExpectation: XCTestExpectation?
496-
var responseReceivedExpectation: XCTestExpectation?
497-
var protocols: [AnyClass]?
522+
let protocols: [AnyClass]?
523+
524+
/* all the following var _XYZ need to be synchronized on syncQ.
525+
We can't just assert that we're on main thread here as we're modified in the URLSessionDataDelegate extension
526+
for DataTask
527+
*/
528+
var _capital = "unknown"
529+
var capital: String {
530+
get {
531+
return self.syncQ.sync { self._capital }
532+
}
533+
set {
534+
self.syncQ.sync { self._capital = newValue }
535+
}
536+
}
537+
var _session: URLSession! = nil
538+
var session: URLSession! {
539+
get {
540+
return self.syncQ.sync { self._session }
541+
}
542+
set {
543+
self.syncQ.sync { self._session = newValue }
544+
}
545+
}
546+
var _task: URLSessionDataTask! = nil
547+
var task: URLSessionDataTask! {
548+
get {
549+
return self.syncQ.sync { self._task }
550+
}
551+
set {
552+
self.syncQ.sync { self._task = newValue }
553+
}
554+
}
555+
var _cancelExpectation: XCTestExpectation?
556+
var cancelExpectation: XCTestExpectation? {
557+
get {
558+
return self.syncQ.sync { self._cancelExpectation }
559+
}
560+
set {
561+
self.syncQ.sync { self._cancelExpectation = newValue }
562+
}
563+
}
564+
var _responseReceivedExpectation: XCTestExpectation?
565+
var responseReceivedExpectation: XCTestExpectation? {
566+
get {
567+
return self.syncQ.sync { self._responseReceivedExpectation }
568+
}
569+
set {
570+
self.syncQ.sync { self._responseReceivedExpectation = newValue }
571+
}
572+
}
498573

499-
public var error = false
574+
private var _error = false
575+
public var error: Bool {
576+
get {
577+
return self.syncQ.sync { self._error }
578+
}
579+
set {
580+
self.syncQ.sync { self._error = newValue }
581+
}
582+
}
500583

501584
init(with expectation: XCTestExpectation, protocolClasses: [AnyClass]? = nil) {
502585
dataTaskExpectation = expectation
503-
protocols = protocolClasses
586+
protocols = protocolClasses
504587
}
505588

506589
func run(with request: URLRequest) {

0 commit comments

Comments
 (0)