@@ -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,32 @@ 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
+ self . cancelInProgress = DispatchSemaphore ( value: 0 )
119
+ // We need to release the stored pointer:
120
+ if let opaque = socketSourcePtr {
121
+ Unmanaged < _SocketSources > . fromOpaque ( opaque) . release ( )
122
+ }
123
+ socketSources? . tearDown ( self . cancelInProgress)
124
+ socketSources = nil
113
125
}
114
- socketSources = nil
115
- }
116
- if let ss = socketSources {
117
- let handler = DispatchWorkItem { [ weak self ] in
118
- self ? . performAction ( for : socket)
126
+ if let ss = socketSources {
127
+ let handler = DispatchWorkItem { [ weak self ] in
128
+ self ? . performAction ( for : socket )
129
+ }
130
+ ss . createSources ( with : action , fileDescriptor : Int ( socket) , queue : self . queue , handler : handler )
119
131
}
120
- ss. createSources ( with: action, fileDescriptor: Int ( socket) , queue: queue, handler: handler)
121
132
}
122
133
return 0
123
134
}
@@ -398,12 +409,18 @@ fileprivate class _SocketSources {
398
409
s. resume ( )
399
410
}
400
411
401
- func tearDown( ) {
412
+ func tearDown( _ cancelInProgress: DispatchSemaphore ? ) {
413
+ let cancelHandler = DispatchWorkItem {
414
+ //the real end of an unregister operation!
415
+ cancelInProgress? . signal ( )
416
+ }
402
417
if let s = readSource {
418
+ s. setCancelHandler ( handler: cancelHandler)
403
419
s. cancel ( )
404
420
}
405
421
readSource = nil
406
422
if let s = writeSource {
423
+ s. setCancelHandler ( handler: cancelHandler)
407
424
s. cancel ( )
408
425
}
409
426
writeSource = nil
0 commit comments