@@ -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 ( )
@@ -72,6 +76,7 @@ fileprivate extension URLSession._MultiHandle {
72
76
}
73
77
}
74
78
79
+
75
80
fileprivate extension URLSession . _MultiHandle {
76
81
/// Forward the libcurl callbacks into Swift methods
77
82
func setupCallbacks( ) {
@@ -99,25 +104,32 @@ fileprivate extension URLSession._MultiHandle {
99
104
// through libdispatch (DispatchSource) and store the source(s) inside
100
105
// a `SocketSources` which we in turn store inside libcurl's multi handle
101
106
// 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 ( )
107
+ commandQueue. async {
108
+ self . cancelInProgress? . wait ( )
109
+ self . cancelInProgress = nil
110
+
111
+ let action = _SocketRegisterAction ( rawValue: CFURLSessionPoll ( value: what) )
112
+ var socketSources = _SocketSources. from ( socketSourcePtr: socketSourcePtr)
113
+ if socketSources == nil && action. needsSource {
114
+ let s = _SocketSources ( )
115
+ let p = Unmanaged . passRetained ( s) . toOpaque ( )
116
+ CFURLSessionMultiHandleAssign ( self . rawHandle, socket, UnsafeMutableRawPointer ( p) )
117
+ socketSources = s
118
+ } else if socketSources != nil && action == . unregister {
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,8 +410,13 @@ fileprivate class _SocketSources {
398
410
s. resume ( )
399
411
}
400
412
401
- func tearDown( ) {
413
+ func tearDown( _ cancelInProgress : DispatchSemaphore ? ) {
402
414
if let s = readSource {
415
+ let cancelHandler = DispatchWorkItem {
416
+ //the real end of an unregister operation!
417
+ cancelInProgress? . signal ( )
418
+ }
419
+ s. setCancelHandler ( handler: cancelHandler)
403
420
s. cancel ( )
404
421
}
405
422
readSource = nil
0 commit comments