Skip to content

Commit 65f9593

Browse files
committed
Swift 3.0, nouv, fixed null current loop
1 parent 85a3928 commit 65f9593

File tree

6 files changed

+100
-43
lines changed

6 files changed

+100
-43
lines changed

RunLoop/CurrentRunLoop.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import Foundation
1818
import Boilerplate
1919

20-
public typealias RunLoopFactory = () -> RunLoopProtocol
20+
public typealias RunLoopFactory = () -> RunLoopProtocol?
2121

2222
private class RunLoopData {
2323
private var _loop:RunLoopProtocol?
@@ -27,27 +27,32 @@ private class RunLoopData {
2727
self.factory = factory
2828
}
2929

30-
var loop:RunLoopProtocol {
30+
var loop:RunLoopProtocol? {
3131
get {
3232
if nil == _loop {
3333
_loop = factory()
3434
}
3535
// yes, it's always value
36-
return _loop!
36+
return _loop
3737
}
3838
}
3939
}
4040

4141
private let _runLoopData = try! ThreadLocal<RunLoopData>()
4242

43-
private func defaultRunLoopFactory() -> RunLoopProtocol {
44-
return Thread.isMain ? RunLoop.main : RunLoop()
43+
private func defaultRunLoopFactory() -> RunLoopProtocol? {
44+
if Thread.isMain {
45+
return RunLoop.main
46+
}
47+
48+
return nil
4549
}
4650

4751
public extension RunLoopProtocol {
48-
public static var current:RunLoopProtocol {
52+
public static var current:RunLoopProtocol? {
4953
get {
5054
var value = _runLoopData.value
55+
5156
if nil == value {
5257
value = RunLoopData(factory: defaultRunLoopFactory)
5358
_runLoopData.value = value

RunLoop/DispatchRunLoop.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
self.init(queue: queue)
8686
}
8787

88+
public class func makeSemaphore(value:Int?, loop:RunLoopProtocol?) -> SemaphoreProtocol {
89+
return value.map({DispatchSemaphore(value: $0)}) ?? DispatchSemaphore()
90+
}
91+
8892
public func semaphore() -> SemaphoreProtocol {
8993
return DispatchSemaphore()
9094
}

RunLoop/RunLoop.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public protocol Settled {
2424
public protocol RunLoopProtocol : NonStrictEquatable {
2525
init()
2626

27-
func semaphore() -> SemaphoreProtocol
28-
func semaphore(value:Int) -> SemaphoreProtocol
27+
//for private use
28+
static func makeSemaphore(value:Int?, loop:RunLoopProtocol?) -> SemaphoreProtocol
2929

3030
/// tries to execute before other tasks
3131
func urgent(task:SafeTask)
@@ -41,6 +41,16 @@ public protocol RunLoopProtocol : NonStrictEquatable {
4141
static var main:RunLoopProtocol {get}
4242
}
4343

44+
public extension RunLoopProtocol {
45+
static func semaphore(value:Int? = nil, loop:RunLoopProtocol? = RunLoop.current) -> SemaphoreProtocol {
46+
guard let semaClass = loop.flatMap({type(of: $0)}) else {
47+
return self.makeSemaphore(value: value, loop: loop)
48+
}
49+
50+
return semaClass.makeSemaphore(value: value, loop: loop)
51+
}
52+
}
53+
4454
public extension RunLoopProtocol {
4555
public static var reactive:Self.Type {
4656
get {

RunLoop/Semaphore.swift

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,29 @@ func ==<T>(lhs:HashableAnyContainer<T>, rhs:HashableAnyContainer<T>) -> Bool {
134134

135135
private enum Wakeable {
136136
case Loop(loop:RunnableRunLoopProtocol)
137-
case Sema(loop:RunLoopProtocol, sema:SemaphoreProtocol)
137+
case LoopedSema(loop:RunLoopProtocol, sema:SemaphoreProtocol)
138+
case Sema(sema:SemaphoreProtocol, leftovers:Array<SafeTask>)
138139
}
139140

140141
private extension Wakeable {
141-
init(loop:RunLoopProtocol) {
142+
init(loop:RunLoopProtocol?) {
143+
guard let loop = loop else {
144+
self = .Sema(sema: RunLoop.semaphore(loop: nil), leftovers:[])
145+
return
146+
}
147+
142148
if let loop = loop as? RunnableRunLoopProtocol {
143149
self = .Loop(loop: loop)
144150
} else {
145-
self = .Sema(loop:loop, sema: loop.semaphore())
151+
self = .LoopedSema(loop:loop, sema: type(of: loop).semaphore(loop: loop))
152+
}
153+
}
154+
155+
private static func wait(sema:SemaphoreProtocol, until:Date?) -> Bool {
156+
if let until = until {
157+
return sema.wait(until: until)
158+
} else {
159+
return sema.wait()
146160
}
147161
}
148162

@@ -154,12 +168,11 @@ private extension Wakeable {
154168
} else {
155169
return loop.run()
156170
}
157-
case .Sema(_, let sema):
158-
if let until = until {
159-
return sema.wait(until: until)
160-
} else {
161-
return sema.wait()
162-
}
171+
//SWIFT BUG: crash
172+
case .Sema(let sema, _)/*, .LoopedSema(_, let sema)*/:
173+
return Wakeable.wait(sema: sema, until: until)
174+
case .LoopedSema(_, let sema):
175+
return Wakeable.wait(sema: sema, until: until)
163176
}
164177
}
165178

@@ -170,11 +183,25 @@ private extension Wakeable {
170183
task()
171184
loop.stop()
172185
}
173-
case .Sema(let loop, let sema):
186+
case .LoopedSema(let loop, let sema):
174187
loop.execute {
175188
task()
176189
let _ = sema.signal()
177190
}
191+
case .Sema(let sema, var leftovers):
192+
leftovers.append(task)
193+
let _ = sema.signal()
194+
}
195+
}
196+
197+
func afterwake() {
198+
switch self {
199+
case .Sema(_, var leftovers):
200+
while !leftovers.isEmpty {
201+
leftovers.removeFirst()()
202+
}
203+
default:
204+
break
178205
}
179206
}
180207
}
@@ -230,6 +257,7 @@ public class RunLoopSemaphore : SemaphoreProtocol {
230257
//}
231258
while !signaled && !timedout {
232259
timedout = wakeable.waitWithConditionalDate(until: until)
260+
wakeable.afterwake()
233261
signaled = self.signaled.value ?? false
234262
}
235263
self.signaled.value = false

RunLoop/Sync.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ public extension RunLoopProtocol {
2727

2828
var result:Result<ReturnType, AnyError>?
2929

30-
let sema = RunLoop.current.semaphore()
30+
let sema = RunLoop.current.map { loop in
31+
type(of: loop).semaphore(loop: loop)
32+
}.getOr {
33+
RunLoop.reactive.semaphore(loop: nil)
34+
}
3135

3236
self.execute {
3337
defer {

Tests/RunLoop/RunLoopTests.swift

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ import Boilerplate
1212
@testable import RunLoop
1313

1414
class RunLoopTests: XCTestCase {
15+
#if !os(Linux) || dispatch
16+
func freshLoop() -> RunLoopProtocol {
17+
let queue = DispatchQueue.global()
18+
let loop = DispatchRunLoop(queue: queue)
19+
return loop
20+
}
21+
#else
22+
//FIXME
23+
func freshLoop() -> RunLoopProtocol {
24+
var loop = RunLoop.reactive.current
25+
let sema = RunLoop.semaphore(loop: loop)
26+
let thread = try! Boilerplate.Thread {
27+
loop = RunLoop.current
28+
sema.signal()
29+
loop.flatMap({$0 as? RunnableRunLoopProtocol})?.run()
30+
}
31+
sema.wait()
32+
}
33+
#endif
1534

1635
#if (os(Linux) && !nouv) || uv
1736
func testUVExecute() {
@@ -74,24 +93,20 @@ class RunLoopTests: XCTestCase {
7493

7594
func testImmediateTimeout() {
7695
let expectation = self.expectation(description: "OK TIMER")
77-
let loop = RunLoop.reactive.current
96+
let loop = RunLoop.reactive.current!
7897
loop.execute(delay: .Immediate) {
7998
expectation.fulfill()
80-
#if os(Linux)
81-
(loop as? RunnableRunLoopType)?.stop()
82-
#endif
99+
(loop as? RunnableRunLoopProtocol)?.stop()
83100
}
84-
#if os(Linux)
85-
(loop as? RunnableRunLoopType)?.run()
86-
#endif
101+
let _ = (loop as? RunnableRunLoopProtocol)?.run()
87102
self.waitForExpectations(timeout: 2, handler: nil)
88103
}
89104

90105
func testNested() {
91106
#if os(Linux)
92-
let rl = RunLoop.current as? RunnableRunLoopType // will be main
107+
let rl = RunLoop.current.flatMap({$0 as? RunnableRunLoopProtocol}) // will be main
93108
#else
94-
let rl = Optional<RunLoopProtocol>(RunLoop.current) // will be main too.
109+
let rl = RunLoop.reactive.current // will be main too.
95110
#endif
96111

97112
print("Current run loop: \(rl)")
@@ -102,21 +117,21 @@ class RunLoopTests: XCTestCase {
102117
rl?.execute {
103118
print("Inner execute called")
104119
inner.fulfill()
105-
#if os(Linux) && !dispatch
120+
#if (os(Linux) && !dispatch) || uv
106121
rl?.stop()
107122
#endif
108123
}
109-
#if os(Linux) && !dispatch
124+
#if (os(Linux) && !dispatch) || uv
110125
rl?.run()
111126
#endif
112127
print("Execute called")
113128
outer.fulfill()
114-
#if os(Linux) && !dispatch
129+
#if (os(Linux) && !dispatch) || uv
115130
rl?.stop()
116131
#endif
117132
}
118133

119-
#if os(Linux) && !dispatch
134+
#if (os(Linux) && !dispatch) || uv
120135
rl?.run(.In(timeout: 2))
121136
#endif
122137

@@ -157,16 +172,9 @@ class RunLoopTests: XCTestCase {
157172
#endif
158173

159174
func testSyncToRunLoop() {
160-
let sema = RunLoop.current.semaphore()
161-
var loop:RunLoopProtocol = RunLoop.current
162-
let thread = try! Boilerplate.Thread {
163-
loop = RunLoop.current
164-
sema.signal()
165-
(loop as? RunnableRunLoopProtocol)?.run()
166-
}
167-
sema.wait()
175+
var loop = freshLoop()
168176

169-
XCTAssertFalse(loop.isEqual(to: RunLoop.current))
177+
XCTAssertFalse(RunLoop.reactive.current.map({$0.isEqual(to: loop)}) ?? false)
170178

171179
let result = loop.sync {
172180
return "result"
@@ -191,8 +199,6 @@ class RunLoopTests: XCTestCase {
191199
XCTFail("shoud not reach this")
192200
}
193201

194-
try! thread.join()
195-
196202
self.waitForExpectations(timeout: 0.1, handler: nil)
197203
}
198204

0 commit comments

Comments
 (0)