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