Skip to content

Commit 214bec2

Browse files
committed
Merge branch 'develop'
2 parents 0acb0dc + f637883 commit 214bec2

File tree

8 files changed

+546
-64
lines changed

8 files changed

+546
-64
lines changed

ExecutionContext.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
658566A31C7CAEE3003B0E08 /* LoopSemaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658566A21C7CAEE3003B0E08 /* LoopSemaphore.swift */; };
1011
659E80891C77450200DE85B1 /* ExecutionContext.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 659E807E1C77450200DE85B1 /* ExecutionContext.framework */; };
1112
659E808E1C77450200DE85B1 /* ExecutionContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659E808D1C77450200DE85B1 /* ExecutionContextTests.swift */; };
1213
659E809E1C77457E00DE85B1 /* DefaultExecutionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659E80981C77457E00DE85B1 /* DefaultExecutionContext.swift */; };
@@ -119,6 +120,7 @@
119120
/* End PBXCopyFilesBuildPhase section */
120121

121122
/* Begin PBXFileReference section */
123+
658566A21C7CAEE3003B0E08 /* LoopSemaphore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopSemaphore.swift; sourceTree = "<group>"; };
122124
659E807E1C77450200DE85B1 /* ExecutionContext.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExecutionContext.framework; sourceTree = BUILT_PRODUCTS_DIR; };
123125
659E80831C77450200DE85B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
124126
659E80881C77450200DE85B1 /* ExecutionContextTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ExecutionContextTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -253,6 +255,7 @@
253255
659E809D1C77457E00DE85B1 /* Result+Some.swift */,
254256
65E646F11C79E0580036D028 /* Semaphore.swift */,
255257
659E80831C77450200DE85B1 /* Info.plist */,
258+
658566A21C7CAEE3003B0E08 /* LoopSemaphore.swift */,
256259
);
257260
path = ExecutionContext;
258261
sourceTree = "<group>";
@@ -610,6 +613,7 @@
610613
659E80A11C77457E00DE85B1 /* ImmediateExecutionContext.swift in Sources */,
611614
659E809E1C77457E00DE85B1 /* DefaultExecutionContext.swift in Sources */,
612615
65E646F21C79E0590036D028 /* Semaphore.swift in Sources */,
616+
658566A31C7CAEE3003B0E08 /* LoopSemaphore.swift in Sources */,
613617
659E80A21C77457E00DE85B1 /* PThreadExecutionContext.swift in Sources */,
614618
);
615619
runOnlyForDeploymentPostprocessing = 0;

ExecutionContext/DefaultExecutionContext.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616

1717
import Foundation
1818

19-
//#if os(Linux)
19+
#if os(Linux)
2020

2121
public typealias DefaultExecutionContext = PThreadExecutionContext
2222

23-
/*#else
23+
#else
2424

2525
public typealias DefaultExecutionContext = DispatchExecutionContext
2626

27-
#endif*/
27+
#endif
2828

2929
public protocol DefaultExecutionContextType : ExecutionContextType {
3030
init(kind:ExecutionContextKind)

ExecutionContext/ExecutionContext.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,11 @@ extension ExecutionContextType {
129129
func syncThroughAsync<ReturnType>(task:() throws -> ReturnType) throws -> ReturnType {
130130
var result:Result<ReturnType, AnyError>?
131131

132-
let sema = Semaphore()
132+
let sema = LoopSemaphore()
133+
sema.willUse()
134+
defer {
135+
sema.didUse()
136+
}
133137

134138
async {
135139
result = materialize(task)
@@ -159,9 +163,9 @@ public func sleep(timeout:Double) {
159163
}
160164

161165
@noreturn public func executionContextMain() {
162-
#if os(Linux)
166+
//#if os(Linux)
163167
RunLoop.runForever()
164-
#else
165-
dispatch_main()
166-
#endif
168+
//#else
169+
// dispatch_main()
170+
//#endif
167171
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//===--- LoopSemaphore.swift -----------------------------------------------===//
2+
//Copyright (c) 2016 Daniel Leping (dileping)
3+
//
4+
//Licensed under the Apache License, Version 2.0 (the "License");
5+
//you may not use this file except in compliance with the License.
6+
//You may obtain a copy of the License at
7+
//
8+
//http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
//Unless required by applicable law or agreed to in writing, software
11+
//distributed under the License is distributed on an "AS IS" BASIS,
12+
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
//See the License for the specific language governing permissions and
14+
//limitations under the License.
15+
//===----------------------------------------------------------------------===//
16+
17+
import Foundation
18+
#if os(Linux)
19+
import CoreFoundation
20+
#endif
21+
22+
#if !os(Linux)
23+
import Dispatch
24+
25+
public class DispatchLoopSemaphore : SemaphoreType {
26+
let sema:dispatch_semaphore_t
27+
28+
public required convenience init() {
29+
self.init(value: 0)
30+
}
31+
32+
public required init(value: Int) {
33+
self.sema = dispatch_semaphore_create(value)
34+
}
35+
36+
public func wait() -> Bool {
37+
return dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER) == 0
38+
}
39+
40+
public func wait(until:NSDate?) -> Bool {
41+
let timeout = until?.timeIntervalSinceNow
42+
return wait(timeout)
43+
}
44+
45+
public func wait(timeout: Double?) -> Bool {
46+
guard let timeout = timeout else {
47+
return wait()
48+
}
49+
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSTimeInterval(NSEC_PER_SEC)))
50+
let result = dispatch_semaphore_wait(sema, time)
51+
return result == 0
52+
}
53+
54+
public func signal() -> Int {
55+
return dispatch_semaphore_signal(sema)
56+
}
57+
}
58+
#endif
59+
60+
extension Optional {
61+
func getOrElse(@autoclosure f:()->Wrapped) -> Wrapped {
62+
switch self {
63+
case .Some(let value):
64+
return value
65+
case .None:
66+
return f()
67+
}
68+
}
69+
}
70+
71+
public class CFRunLoopSemaphore : SemaphoreType {
72+
var source: RunLoopSource?
73+
var signaled: Bool
74+
75+
private(set) public var value: Int
76+
77+
/// Creates a new semaphore with the given initial value
78+
/// See NSCondition and https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW13
79+
public required init(value: Int) {
80+
self.value = value
81+
self.signaled = false
82+
self.source = RunLoopSource( { [unowned self] in
83+
self.signaled = true
84+
self.value += 1
85+
// On linux timeout not working in run loop
86+
#if os(Linux)
87+
CFRunLoopStop(CFRunLoopGetCurrent())
88+
#endif
89+
}, priority: 2)
90+
}
91+
92+
/// Creates a new semaphore with initial value 0
93+
/// This kind of semaphores is useful to protect a critical section
94+
public convenience required init() {
95+
self.init(value: 0)
96+
}
97+
98+
public func wait() -> Bool {
99+
return wait(nil)
100+
}
101+
102+
/// returns true on success (false if timeout expired)
103+
/// if nil is passed - waits forever
104+
public func wait(until:NSDate?) -> Bool {
105+
let until = until.getOrElse(NSDate.distantFuture())
106+
107+
defer {
108+
self.signaled = false
109+
}
110+
111+
var timedout:Bool = false
112+
113+
while value <= 0 {
114+
while !self.signaled && !timedout {
115+
RunLoop.runUntilOnce(RunLoop.defaultMode, until: until)
116+
timedout = until.timeIntervalSinceNow <= 0
117+
}
118+
if timedout {
119+
break
120+
}
121+
}
122+
123+
if signaled {
124+
value -= 1
125+
}
126+
127+
return signaled
128+
}
129+
130+
/// Performs the signal operation on this semaphore
131+
public func signal() -> Int {
132+
source?.signal()
133+
return value
134+
}
135+
136+
public func willUse() {
137+
let loop:RunLoop = RunLoop.currentRunLoop()
138+
loop.addSource(source!, mode: RunLoop.defaultMode)
139+
}
140+
141+
public func didUse() {
142+
let loop:RunLoop = RunLoop.currentRunLoop()
143+
loop.removeSource(source!, mode: RunLoop.defaultMode)
144+
}
145+
}
146+
147+
#if os(Linux)
148+
public typealias LoopSemaphore = CFRunLoopSemaphore
149+
#else
150+
public typealias LoopSemaphore = DispatchLoopSemaphore
151+
#endif

ExecutionContext/PThreadExecutionContext.swift

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,18 @@
3030
pthread.task?()
3131
return nil
3232
}
33-
34-
private class PThread {
33+
34+
internal class PThreadKey {
35+
private var key: pthread_key_t = 0
36+
init(destructionCallback: (@convention(c) UnsafeMutablePointer<Void> -> Void)! = nil) {
37+
pthread_key_create(&key, destructionCallback)
38+
}
39+
deinit {
40+
pthread_key_delete(key)
41+
}
42+
}
43+
44+
internal class PThread {
3545
let thread: UnsafeMutablePointer<pthread_t>
3646
let task:SafeTask?
3747

@@ -47,6 +57,33 @@
4757
func start() {
4858
pthread_create(thread, nil, thread_proc, UnsafeMutablePointer<Void>(Unmanaged.passRetained(self).toOpaque()))
4959
}
60+
61+
static func getSpecific(key: PThreadKey) -> AnyObject? {
62+
let val = pthread_getspecific(key.key)
63+
if val == nil {
64+
return nil
65+
}
66+
return Unmanaged<AnyObject>.fromOpaque(COpaquePointer(val)).takeUnretainedValue()
67+
}
68+
69+
static func setSpecific(obj: AnyObject?, key: PThreadKey, retain: Bool = false) {
70+
if retain {
71+
let old = pthread_getspecific(key.key)
72+
if old != nil {
73+
Unmanaged<AnyObject>.fromOpaque(COpaquePointer(old)).release()
74+
}
75+
}
76+
if obj == nil {
77+
pthread_setspecific(key.key, nil)
78+
} else {
79+
if retain {
80+
pthread_setspecific(key.key, UnsafePointer<Void>(Unmanaged.passRetained(obj!).toOpaque()))
81+
} else {
82+
pthread_setspecific(key.key, UnsafePointer<Void>(Unmanaged.passUnretained(obj!).toOpaque()))
83+
}
84+
}
85+
}
86+
5087
}
5188

5289
private class ParallelContext : ExecutionContextBase, ExecutionContextType {
@@ -67,31 +104,46 @@
67104
return try syncThroughAsync(task)
68105
}
69106
}
107+
108+
// This class is workaround around retain cycle in pthread run loop creation. See below in init(). Stupid ARC :(
109+
private class RunLoopHolder {
110+
var loop: RunLoop? = nil
111+
}
70112

71113
private class SerialContext : ExecutionContextBase, ExecutionContextType {
72114
private let rl:RunLoop
73115

74116
override init() {
75-
var runLoop:RunLoop?
117+
let holder = RunLoopHolder()
76118
let sema = Semaphore()
119+
sema.willUse()
120+
defer {
121+
sema.didUse()
122+
}
77123

78-
PThread(task: {
79-
runLoop = RunLoop.currentRunLoop(true)
124+
PThread(task: { [unowned holder] in
125+
holder.loop = RunLoop.currentRunLoop()
126+
holder.loop!.startTaskQueue()
80127
sema.signal()
81128
RunLoop.run()
82129
}).start()
83130

84131
sema.wait()
85132

86-
self.rl = runLoop!
133+
self.rl = holder.loop!
87134
}
88135

89136
init(runLoop:RunLoop) {
90137
rl = runLoop
138+
rl.startTaskQueue()
139+
}
140+
141+
deinit {
142+
rl.stopTaskQueue()
91143
}
92144

93145
func async(task:SafeTask) {
94-
rl.addSource(RunLoopSource(task), mode: RunLoop.defaultMode)
146+
rl.addTask(task)
95147
}
96148

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

0 commit comments

Comments
 (0)