@@ -71,17 +71,13 @@ import CoreFoundation
71
71
}
72
72
}
73
73
74
- internal protocol WakeableRunLoop : AnyObject {
75
- func wakeUp( )
76
- }
77
-
78
74
private class RunLoopCallbackInfo {
79
- private var task : SafeTask
80
- private var runLoops : [ WakeableRunLoop ] = [ ]
75
+ private let task : SafeTask
76
+ private var runLoops : [ RunLoop ] = [ ]
81
77
82
- init ( _ task: SafeTask ) {
78
+ init ( _ task: SafeTask ) {
83
79
self . task = task
84
- }
80
+ }
85
81
86
82
func run( ) {
87
83
task ( )
@@ -133,13 +129,27 @@ import CoreFoundation
133
129
}
134
130
}
135
131
136
- init ( _ task: SafeTask , priority: Int = 0 ) {
137
- self . info = RunLoopCallbackInfo ( task)
138
- self . priority = priority
132
+ init ( _ task: SafeTask , priority: Int = 0 , runOnce: Bool = false ) {
133
+ self . priority = priority
134
+ if runOnce {
135
+ var stopTask : SafeTask ?
136
+ self . info = RunLoopCallbackInfo ( {
137
+ task ( )
138
+ stopTask ? ( )
139
+ } )
140
+ stopTask = { self . stop ( ) }
141
+ } else {
142
+ self . info = RunLoopCallbackInfo ( task)
143
+ }
144
+
139
145
}
140
146
141
147
deinit {
142
- if _source != nil && CFRunLoopSourceIsValid ( _source) {
148
+ stop ( )
149
+ }
150
+
151
+ func stop( ) {
152
+ if _source != nil && CFRunLoopSourceIsValid ( _source) {
143
153
CFRunLoopSourceInvalidate ( _source)
144
154
_source = nil
145
155
}
@@ -155,6 +165,24 @@ import CoreFoundation
155
165
}
156
166
}
157
167
168
+ class RunLoopTaskQueueSource : RunLoopSource {
169
+ private let queue = TaskQueue ( )
170
+
171
+ init ( priority: Int = 1 ) {
172
+ super. init ( { [ unowned queue] in
173
+ if let element = queue. dequeue ( ) {
174
+ element. run ( )
175
+ element. source. signal ( )
176
+ }
177
+ } , priority: priority)
178
+ }
179
+
180
+ func addTask( task: SafeTask ) {
181
+ queue. enqueue ( TaskQueueElement ( task, runLoopSource: self ) )
182
+ self . signal ( )
183
+ }
184
+ }
185
+
158
186
private func timerRunCallback( timer: CFRunLoopTimer ! , i: UnsafeMutablePointer < Void > ) {
159
187
runLoopCallbackInfoRun ( i)
160
188
}
@@ -186,66 +214,40 @@ import CoreFoundation
186
214
}
187
215
}
188
216
189
- private class CFRunLoopWakeupHolder : WakeableRunLoop {
190
- private let loop : CFRunLoop !
191
-
192
- init ( loop: CFRunLoop ! ) {
193
- self . loop = loop
194
- }
195
-
196
- func wakeUp( ) {
197
- CFRunLoopWakeUp ( loop)
198
- }
199
- }
200
-
201
- class RunLoop : WakeableRunLoop {
217
+ class RunLoop {
202
218
private let cfRunLoop : CFRunLoop !
203
219
204
- private var taskQueueSource : RunLoopSource
205
- private var taskQueue : TaskQueue
220
+ private var taskQueueSource : RunLoopTaskQueueSource ? = nil
221
+ private let taskQueueLock = NSLock ( )
206
222
207
223
#if !os(Linux)
208
224
static let defaultMode : NSString = " kCFRunLoopDefaultMode " as NSString
209
225
#else
210
226
static let defaultMode : NSString = " kCFRunLoopDefaultMode " . bridge ( )
211
227
#endif
212
228
213
- private static let threadKey = PThreadKey ( )
229
+ private static let threadKey = PThreadKey ( destructionCallback: { loop in
230
+ Unmanaged < RunLoop > . fromOpaque ( COpaquePointer ( loop) ) . release ( )
231
+ } )
214
232
215
233
private static let MainRunLoop = RunLoop . createMainRunLoop ( )
216
234
217
235
init ( _ cfRunLoop: CFRunLoop ) {
218
236
self . cfRunLoop = cfRunLoop
219
-
220
- let queue = TaskQueue ( )
221
-
222
- taskQueueSource = RunLoopSource ( {
223
- if let element = queue. dequeue ( ) {
224
- element. run ( )
225
- element. source. signal ( )
226
- }
227
- } )
228
- taskQueue = queue
229
- addSource ( taskQueueSource, mode: RunLoop . defaultMode, retainLoop: false )
230
- }
231
-
232
- deinit {
233
- addTask {
234
- PThread . setSpecific ( nil , key: RunLoop . threadKey)
235
- }
236
237
}
238
+
237
239
convenience init ( _ runLoop: AnyObject ) {
238
240
self . init ( unsafeBitCast ( runLoop, CFRunLoop . self) )
239
241
}
240
242
241
243
private static func createMainRunLoop( ) -> RunLoop {
242
244
let runLoop = RunLoop ( CFRunLoopGetMain ( ) )
243
245
if runLoop. isCurrent ( ) {
244
- PThread . setSpecific ( runLoop, key: RunLoop . threadKey)
246
+ PThread . setSpecific ( runLoop, key: RunLoop . threadKey, retain : true )
245
247
} else {
246
- let sema = Semaphore ( )
248
+ let sema = LoopSemaphore ( )
247
249
runLoop. addTask ( {
248
- PThread . setSpecific ( runLoop, key: RunLoop . threadKey)
250
+ PThread . setSpecific ( runLoop, key: RunLoop . threadKey, retain : true )
249
251
sema. signal ( )
250
252
} )
251
253
sema. wait ( )
@@ -256,7 +258,7 @@ import CoreFoundation
256
258
static func currentRunLoop( ) -> RunLoop {
257
259
guard let loop = PThread . getSpecific ( RunLoop . threadKey) else {
258
260
let loop = RunLoop ( CFRunLoopGetCurrent ( ) )
259
- PThread . setSpecific ( loop, key: RunLoop . threadKey)
261
+ PThread . setSpecific ( loop, key: RunLoop . threadKey, retain : true )
260
262
return loop
261
263
}
262
264
return unsafeBitCast ( loop, RunLoop . self)
@@ -266,6 +268,26 @@ import CoreFoundation
266
268
return MainRunLoop
267
269
}
268
270
271
+ func startTaskQueue( priority: Int = 1 ) {
272
+ print ( " Start queue called " )
273
+ defer {
274
+ taskQueueLock. unlock ( )
275
+ }
276
+ taskQueueLock. lock ( )
277
+ self . taskQueueSource = RunLoopTaskQueueSource ( )
278
+
279
+ addSource ( taskQueueSource!, mode: RunLoop . defaultMode)
280
+ }
281
+
282
+ func stopTaskQueue( ) {
283
+ defer {
284
+ taskQueueLock. unlock ( )
285
+ }
286
+ taskQueueLock. lock ( )
287
+ self . taskQueueSource = nil
288
+ }
289
+
290
+
269
291
func isCurrent( ) -> Bool {
270
292
return cfRunLoop === CFRunLoopGetCurrent ( )
271
293
}
@@ -307,15 +329,11 @@ import CoreFoundation
307
329
while true { run ( ) }
308
330
}
309
331
310
- func addSource( rls: RunLoopSource , mode: NSString , retainLoop : Bool = true ) {
332
+ func addSource( rls: RunLoopSource , mode: NSString ) {
311
333
let crls = unsafeBitCast ( rls. cfObject, CFRunLoopSource . self)
312
334
if CFRunLoopSourceIsValid ( crls) {
313
335
CFRunLoopAddSource ( cfRunLoop, crls, mode. cfString)
314
- if retainLoop {
315
- rls. info. runLoops. append ( self )
316
- } else {
317
- rls. info. runLoops. append ( CFRunLoopWakeupHolder ( loop: cfRunLoop) )
318
- }
336
+ rls. info. runLoops. append ( self )
319
337
wakeUp ( )
320
338
}
321
339
}
@@ -330,8 +348,17 @@ import CoreFoundation
330
348
}
331
349
332
350
func addTask( task: SafeTask ) {
333
- taskQueue. enqueue ( TaskQueueElement ( task, runLoopSource: taskQueueSource) )
334
- taskQueueSource. signal ( )
351
+ defer {
352
+ taskQueueLock. unlock ( )
353
+ }
354
+ taskQueueLock. lock ( )
355
+ if let queue = taskQueueSource {
356
+ queue. addTask ( task)
357
+ } else {
358
+ let source = RunLoopSource ( task, priority: 0 , runOnce: true )
359
+ addSource ( source, mode: RunLoop . defaultMode)
360
+ source. signal ( )
361
+ }
335
362
}
336
363
337
364
func wakeUp( ) {
0 commit comments