@@ -39,7 +39,11 @@ extension URLSession {
39
39
let group = DispatchGroup ( )
40
40
fileprivate var easyHandles : [ _EasyHandle ] = [ ]
41
41
fileprivate var timeoutSource : _TimeoutSource ? = nil
42
-
42
+
43
+ //SR-4567: we need to synchronize the register/unregister commands to the epoll machinery in libdispatch
44
+ fileprivate let commandQueue : DispatchQueue = DispatchQueue ( label: " Register-unregister synchronization " )
45
+ fileprivate var cancelInProgress : DispatchSemaphore ? = nil
46
+
43
47
init ( configuration: URLSession . _Configuration , workQueue: DispatchQueue ) {
44
48
queue = DispatchQueue ( label: " MultiHandle.isolation " , target: workQueue)
45
49
setupCallbacks ( )
@@ -99,25 +103,33 @@ fileprivate extension URLSession._MultiHandle {
99
103
// through libdispatch (DispatchSource) and store the source(s) inside
100
104
// a `SocketSources` which we in turn store inside libcurl's multi handle
101
105
// by means of curl_multi_assign() -- we retain the object fist.
102
- let action = _SocketRegisterAction ( rawValue: CFURLSessionPoll ( value: what) )
103
- var socketSources = _SocketSources. from ( socketSourcePtr: socketSourcePtr)
104
- if socketSources == nil && action. needsSource {
105
- let s = _SocketSources ( )
106
- let p = Unmanaged . passRetained ( s) . toOpaque ( )
107
- CFURLSessionMultiHandleAssign ( rawHandle, socket, UnsafeMutableRawPointer ( p) )
108
- socketSources = s
109
- } else if socketSources != nil && action == . unregister {
110
- // We need to release the stored pointer:
111
- if let opaque = socketSourcePtr {
112
- Unmanaged < _SocketSources > . fromOpaque ( opaque) . release ( )
106
+ commandQueue. async {
107
+ self . cancelInProgress? . wait ( )
108
+ self . cancelInProgress = nil
109
+
110
+ let action = _SocketRegisterAction ( rawValue: CFURLSessionPoll ( value: what) )
111
+ var socketSources = _SocketSources. from ( socketSourcePtr: socketSourcePtr)
112
+ if socketSources == nil && action. needsSource {
113
+ let s = _SocketSources ( )
114
+ let p = Unmanaged . passRetained ( s) . toOpaque ( )
115
+ CFURLSessionMultiHandleAssign ( self . rawHandle, socket, UnsafeMutableRawPointer ( p) )
116
+ socketSources = s
117
+ } else if socketSources != nil && action == . unregister {
118
+ //the beginning of an unregister operation
119
+ self . cancelInProgress = DispatchSemaphore ( value: 0 )
120
+ // We need to release the stored pointer:
121
+ if let opaque = socketSourcePtr {
122
+ Unmanaged < _SocketSources > . fromOpaque ( opaque) . release ( )
123
+ }
124
+ socketSources? . tearDown ( self . cancelInProgress)
125
+ socketSources = nil
113
126
}
114
- socketSources = nil
115
- }
116
- if let ss = socketSources {
117
- let handler = DispatchWorkItem { [ weak self ] in
118
- self ? . performAction ( for : socket)
127
+ if let ss = socketSources {
128
+ let handler = DispatchWorkItem { [ weak self ] in
129
+ self ? . performAction ( for : socket )
130
+ }
131
+ ss . createSources ( with : action , fileDescriptor : Int ( socket) , queue : self . queue , handler : handler )
119
132
}
120
- ss. createSources ( with: action, fileDescriptor: Int ( socket) , queue: queue, handler: handler)
121
133
}
122
134
return 0
123
135
}
@@ -398,12 +410,18 @@ fileprivate class _SocketSources {
398
410
s. resume ( )
399
411
}
400
412
401
- func tearDown( ) {
413
+ func tearDown( _ cancelInProgress: DispatchSemaphore ? ) {
414
+ let cancelHandler = DispatchWorkItem {
415
+ //the real end of an unregister operation!
416
+ cancelInProgress? . signal ( )
417
+ }
402
418
if let s = readSource {
419
+ s. setCancelHandler ( handler: cancelHandler)
403
420
s. cancel ( )
404
421
}
405
422
readSource = nil
406
423
if let s = writeSource {
424
+ s. setCancelHandler ( handler: cancelHandler)
407
425
s. cancel ( )
408
426
}
409
427
writeSource = nil
0 commit comments