Skip to content

Commit 0c551b8

Browse files
committed
Merge pull request #7 from ypopovych/master
New async implementation
2 parents 512e424 + 25dd1e6 commit 0c551b8

File tree

2 files changed

+118
-29
lines changed

2 files changed

+118
-29
lines changed

ExecutionContext/PThreadExecutionContext.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,15 @@
8383

8484
sema.wait()
8585

86-
self.rl = RunLoop(runLoop!, autoStop: true)
86+
self.rl = RunLoop(runLoop!)
8787
}
8888

8989
init(runLoop:RunLoop) {
9090
rl = runLoop
9191
}
9292

9393
func async(task:SafeTask) {
94-
rl.addSource(RunLoopSource(task), mode: RunLoop.defaultMode)
94+
rl.addTask(task)
9595
}
9696

9797
func async(after:Double, task:SafeTask) {

ExecutionContext/RunLoop.swift

Lines changed: 116 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,54 @@ import CoreFoundation
2323
var cfString: CFString { return unsafeBitCast(self, CFString.self) }
2424
}
2525

26+
private class TaskQueueElement {
27+
private let task : SafeTask
28+
private let source: RunLoopSource
29+
var next: TaskQueueElement? = nil
30+
31+
init(_ task: SafeTask, runLoopSource: RunLoopSource) {
32+
self.task = task
33+
self.source = runLoopSource
34+
}
35+
36+
func run() {
37+
task()
38+
}
39+
}
40+
41+
private class TaskQueue {
42+
private let lock = NSLock()
43+
private var head:TaskQueueElement? = nil
44+
private var tail:TaskQueueElement? = nil
45+
46+
func enqueue(elem: TaskQueueElement) {
47+
defer {
48+
lock.unlock()
49+
}
50+
lock.lock()
51+
if tail == nil {
52+
head = elem
53+
tail = elem
54+
} else {
55+
tail!.next = elem
56+
tail = elem
57+
}
58+
}
59+
60+
func dequeue() -> TaskQueueElement? {
61+
defer {
62+
lock.unlock()
63+
}
64+
lock.lock()
65+
let elem = head
66+
head = head?.next
67+
if head == nil {
68+
tail = nil
69+
}
70+
return elem
71+
}
72+
}
73+
2674
private class RunLoopCallbackInfo {
2775
private var task: SafeTask
2876
private var runLoops: [RunLoop] = []
@@ -37,10 +85,19 @@ import CoreFoundation
3785
}
3886

3987
private func runLoopCallbackInfoRun(i: UnsafeMutablePointer<Void>) {
40-
let info = Unmanaged<RunLoopCallbackInfo>.fromOpaque(COpaquePointer(i)).takeRetainedValue()
88+
let info = Unmanaged<RunLoopCallbackInfo>.fromOpaque(COpaquePointer(i)).takeUnretainedValue()
4189
info.run()
4290
}
4391

92+
private func runLoopCallbackInfoRetain(i: UnsafePointer<Void>) -> UnsafePointer<Void> {
93+
Unmanaged<RunLoopCallbackInfo>.fromOpaque(COpaquePointer(i)).retain()
94+
return i
95+
}
96+
97+
private func runLoopCallbackInfoRelease(i: UnsafePointer<Void>) {
98+
Unmanaged<RunLoopCallbackInfo>.fromOpaque(COpaquePointer(i)).release()
99+
}
100+
44101
private protocol RunLoopCallback {
45102
var info : RunLoopCallbackInfo { get }
46103
var cfObject: AnyObject { get }
@@ -56,9 +113,9 @@ import CoreFoundation
56113
if _source == nil {
57114
var context = CFRunLoopSourceContext(
58115
version: 0,
59-
info: UnsafeMutablePointer<Void>(Unmanaged.passRetained(info).toOpaque()),
60-
retain: nil,
61-
release: nil,
116+
info: UnsafeMutablePointer<Void>(Unmanaged.passUnretained(info).toOpaque()),
117+
retain: runLoopCallbackInfoRetain,
118+
release: runLoopCallbackInfoRelease,
62119
copyDescription: nil,
63120
equal: nil,
64121
hash: nil,
@@ -76,6 +133,19 @@ import CoreFoundation
76133
self.info = RunLoopCallbackInfo(task)
77134
self.priority = priority
78135
}
136+
137+
deinit {
138+
if _source != nil && CFRunLoopSourceIsValid(_source) {
139+
CFRunLoopSourceInvalidate(_source)
140+
_source = nil
141+
}
142+
}
143+
144+
func signal() {
145+
if _source != nil {
146+
CFRunLoopSourceSignal(_source)
147+
}
148+
}
79149
}
80150

81151
private func timerRunCallback(timer: CFRunLoopTimer!, i: UnsafeMutablePointer<Void>) {
@@ -92,9 +162,9 @@ import CoreFoundation
92162
if _timer == nil {
93163
var context = CFRunLoopTimerContext(
94164
version: 0,
95-
info: UnsafeMutablePointer<Void>(Unmanaged.passRetained(info).toOpaque()),
96-
retain: nil,
97-
release: nil,
165+
info: UnsafeMutablePointer<Void>(Unmanaged.passUnretained(info).toOpaque()),
166+
retain: runLoopCallbackInfoRetain,
167+
release: runLoopCallbackInfoRelease,
98168
copyDescription: nil
99169
)
100170
_timer = CFRunLoopTimerCreate(nil, CFAbsoluteTimeGetCurrent()+delay, -1, 0, 0, timerRunCallback, &context)
@@ -111,35 +181,42 @@ import CoreFoundation
111181

112182
class RunLoop {
113183
private let cfRunLoop: CFRunLoop!
114-
private let autoStop: Bool
184+
185+
private var taskQueueSource: RunLoopSource
186+
private var taskQueue: TaskQueue
115187

116188
#if !os(Linux)
117189
static let defaultMode:NSString = "kCFRunLoopDefaultMode" as NSString
118190
#else
119191
static let defaultMode:NSString = "kCFRunLoopDefaultMode".bridge()
120192
#endif
121193

122-
init(_ cfRunLoop: CFRunLoop, autoStop: Bool = true) {
194+
init(_ cfRunLoop: CFRunLoop) {
123195
self.cfRunLoop = cfRunLoop
124-
self.autoStop = autoStop
196+
197+
let queue = TaskQueue()
198+
199+
taskQueueSource = RunLoopSource({
200+
var elem = queue.dequeue()
201+
while elem != nil {
202+
elem!.run()
203+
elem = queue.dequeue()
204+
}
205+
})
206+
taskQueue = queue
207+
addSource(taskQueueSource, mode: RunLoop.defaultMode, retainLoop: false)
125208
}
126209

127-
convenience init(_ runLoop: AnyObject, autoStop: Bool = true) {
128-
self.init(unsafeBitCast(runLoop, CFRunLoop.self), autoStop: autoStop)
210+
convenience init(_ runLoop: AnyObject) {
211+
self.init(unsafeBitCast(runLoop, CFRunLoop.self))
129212
}
130213

131-
deinit {
132-
if autoStop && cfRunLoop != nil {
133-
CFRunLoopStop(cfRunLoop)
134-
}
135-
}
136-
137-
static func currentRunLoop(autoStop: Bool = false) -> RunLoop {
138-
return RunLoop(CFRunLoopGetCurrent(), autoStop: autoStop)
214+
static func currentRunLoop() -> RunLoop {
215+
return RunLoop(CFRunLoopGetCurrent())
139216
}
140217

141218
static func mainRunLoop() -> RunLoop {
142-
return RunLoop(CFRunLoopGetMain(), autoStop: false)
219+
return RunLoop(CFRunLoopGetMain())
143220
}
144221

145222
static func currentCFRunLoop() -> AnyObject {
@@ -156,33 +233,45 @@ import CoreFoundation
156233

157234
static func runInMode(mode: NSString) {
158235
#if !os(Linux)
159-
while CFRunLoopRunInMode(mode.cfString, Double.infinity, false) != .Stopped {}
236+
var result:CFRunLoopRunResult
237+
repeat {
238+
result = CFRunLoopRunInMode(mode.cfString, Double.infinity, false)
239+
} while result != .Finished && result != .Stopped
160240
#else
161-
while CFRunLoopRunInMode(mode.cfString, Double.infinity, false) != Int32(kCFRunLoopRunStopped) {}
241+
var result:Int32
242+
repeat {
243+
result = CFRunLoopRunInMode(mode.cfString, Double.infinity, false)
244+
} while result != Int32(kCFRunLoopRunStopped) && result != Int32(kCFRunLoopRunFinished)
162245
#endif
163246
}
164247

165248
@noreturn static func runForever() {
166249
while true { run() }
167250
}
168251

169-
func addSource(rls: RunLoopSource, mode: NSString) {
252+
func addSource(rls: RunLoopSource, mode: NSString, retainLoop: Bool = true) {
170253
let crls = unsafeBitCast(rls.cfObject, CFRunLoopSource.self)
171254
if CFRunLoopSourceIsValid(crls) {
172255
CFRunLoopAddSource(cfRunLoop, crls, mode.cfString)
173-
rls.info.runLoops.append(self)
174-
CFRunLoopSourceSignal(crls)
256+
if retainLoop { rls.info.runLoops.append(self) }
257+
rls.signal()
175258
CFRunLoopWakeUp(cfRunLoop)
176259
}
177260
}
178261

179262
func addDelay(rld: RunLoopDelay, mode: NSString) {
180263
let crld = unsafeBitCast(rld.cfObject, CFRunLoopTimer.self)
181-
if CFRunLoopTimerIsValid(crld) {
264+
if CFRunLoopTimerIsValid(crld) && (rld.info.runLoops.count == 0 || rld.info.runLoops[0] === self) {
182265
CFRunLoopAddTimer(cfRunLoop, crld, mode.cfString)
183266
rld.info.runLoops.append(self)
184267
CFRunLoopWakeUp(cfRunLoop)
185268
}
186269
}
270+
271+
func addTask(task: SafeTask) {
272+
taskQueue.enqueue(TaskQueueElement(task, runLoopSource: taskQueueSource))
273+
taskQueueSource.signal()
274+
CFRunLoopWakeUp(cfRunLoop)
275+
}
187276
}
188277
//#endif

0 commit comments

Comments
 (0)