Skip to content

Commit 4038741

Browse files
committed
Added semaphore
1 parent f2847cf commit 4038741

File tree

3 files changed

+114
-3
lines changed

3 files changed

+114
-3
lines changed

ExecutionContext.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
65E646DE1C7994A00036D028 /* CustomExecutionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646DC1C79949C0036D028 /* CustomExecutionContext.swift */; };
3030
65E646DF1C7994A10036D028 /* CustomExecutionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646DC1C79949C0036D028 /* CustomExecutionContext.swift */; };
3131
65E646E01C7994A20036D028 /* CustomExecutionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646DC1C79949C0036D028 /* CustomExecutionContext.swift */; };
32+
65E646F21C79E0590036D028 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646F11C79E0580036D028 /* Semaphore.swift */; };
33+
65E646F31C79E0590036D028 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646F11C79E0580036D028 /* Semaphore.swift */; };
34+
65E646F41C79E0590036D028 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646F11C79E0580036D028 /* Semaphore.swift */; };
35+
65E646F51C79E0590036D028 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646F11C79E0580036D028 /* Semaphore.swift */; };
3236
65FB86901C78A8330005CD1B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80E61C78A70700DE85B1 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3337
65FB86921C78A8D30005CD1B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80E31C78A70700DE85B1 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3438
65FB86A21C78AA400005CD1B /* ExecutionContext.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65FB86981C78AA400005CD1B /* ExecutionContext.framework */; };
@@ -141,6 +145,7 @@
141145
659E80F01C78A70700DE85B1 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
142146
659E80F11C78A70700DE85B1 /* Result.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; path = Result.framework.dSYM; sourceTree = "<group>"; };
143147
65E646DC1C79949C0036D028 /* CustomExecutionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomExecutionContext.swift; sourceTree = "<group>"; };
148+
65E646F11C79E0580036D028 /* Semaphore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Semaphore.swift; sourceTree = "<group>"; };
144149
65FB86981C78AA400005CD1B /* ExecutionContext.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExecutionContext.framework; sourceTree = BUILT_PRODUCTS_DIR; };
145150
65FB86A11C78AA400005CD1B /* ExecutionContextTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ExecutionContextTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
146151
65FB86BE1C78AC260005CD1B /* ExecutionContext.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExecutionContext.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -240,6 +245,7 @@
240245
659E809B1C77457E00DE85B1 /* ImmediateExecutionContext.swift */,
241246
65E646DC1C79949C0036D028 /* CustomExecutionContext.swift */,
242247
659E809D1C77457E00DE85B1 /* Result+Some.swift */,
248+
65E646F11C79E0580036D028 /* Semaphore.swift */,
243249
659E80831C77450200DE85B1 /* Info.plist */,
244250
);
245251
path = ExecutionContext;
@@ -596,6 +602,7 @@
596602
659E80A31C77457E00DE85B1 /* Result+Some.swift in Sources */,
597603
659E80A11C77457E00DE85B1 /* ImmediateExecutionContext.swift in Sources */,
598604
659E809E1C77457E00DE85B1 /* DefaultExecutionContext.swift in Sources */,
605+
65E646F21C79E0590036D028 /* Semaphore.swift in Sources */,
599606
659E80A21C77457E00DE85B1 /* PThreadExecutionContext.swift in Sources */,
600607
);
601608
runOnlyForDeploymentPostprocessing = 0;
@@ -618,6 +625,7 @@
618625
659E80DC1C78A4BC00DE85B1 /* Result+Some.swift in Sources */,
619626
659E80DB1C78A4B800DE85B1 /* ImmediateExecutionContext.swift in Sources */,
620627
659E80D71C78A4AB00DE85B1 /* ExecutionContext.swift in Sources */,
628+
65E646F31C79E0590036D028 /* Semaphore.swift in Sources */,
621629
659E80D91C78A4B200DE85B1 /* DispatchExecutionContext.swift in Sources */,
622630
);
623631
runOnlyForDeploymentPostprocessing = 0;
@@ -640,6 +648,7 @@
640648
65FB86B41C78AAE50005CD1B /* Result+Some.swift in Sources */,
641649
65FB86B31C78AAE50005CD1B /* ImmediateExecutionContext.swift in Sources */,
642650
65FB86AF1C78AADB0005CD1B /* ExecutionContext.swift in Sources */,
651+
65E646F41C79E0590036D028 /* Semaphore.swift in Sources */,
643652
65FB86B11C78AAE50005CD1B /* DispatchExecutionContext.swift in Sources */,
644653
);
645654
runOnlyForDeploymentPostprocessing = 0;
@@ -662,6 +671,7 @@
662671
65FB86CB1C78AC8B0005CD1B /* Result+Some.swift in Sources */,
663672
65FB86CA1C78AC8B0005CD1B /* ImmediateExecutionContext.swift in Sources */,
664673
65FB86C61C78AC8B0005CD1B /* ExecutionContext.swift in Sources */,
674+
65E646F51C79E0590036D028 /* Semaphore.swift in Sources */,
665675
65FB86C81C78AC8B0005CD1B /* DispatchExecutionContext.swift in Sources */,
666676
);
667677
runOnlyForDeploymentPostprocessing = 0;

ExecutionContext/Semaphore.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===--- Semaphore.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+
19+
private extension NSCondition {
20+
func waitWithConditionalEnd(date:NSDate?) -> Bool {
21+
guard let date = date else {
22+
self.wait()
23+
return true
24+
}
25+
return self.waitUntilDate(date)
26+
}
27+
}
28+
29+
/// A wrapper around NSCondition
30+
public class Semaphore {
31+
32+
/// The underlying NSCondition
33+
private(set) public var underlyingSemaphore: NSCondition
34+
private(set) public var value: Int
35+
36+
/// Creates a new semaphore with the given initial value
37+
/// See NSCondition and https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW13
38+
public init(value: Int) {
39+
self.underlyingSemaphore = NSCondition()
40+
self.value = value
41+
}
42+
43+
/// Creates a new semaphore with initial value 1
44+
/// This kind of semaphores is useful to protect a critical section
45+
public convenience init() {
46+
self.init(value: 1)
47+
}
48+
49+
/// returns true on success (false if timeout expired)
50+
/// if nil is passed - waits forever
51+
public func wait(until:NSDate? = nil) -> Bool {
52+
underlyingSemaphore.lock()
53+
defer {
54+
value -= 1
55+
underlyingSemaphore.unlock()
56+
}
57+
58+
var signaled:Bool = true
59+
while value <= 0 {
60+
signaled = underlyingSemaphore.waitWithConditionalEnd(until)
61+
if !signaled {
62+
break
63+
}
64+
}
65+
66+
return signaled
67+
}
68+
69+
/// Performs the wait operation on this semaphore until the timeout
70+
/// Returns true if the semaphore was signalled before the timeout occurred
71+
/// or false if the timeout occurred.
72+
public func wait(timeout: Double?) -> Bool {
73+
let until = timeout.flatMap {$0 <= 0 ? nil : $0}.map{NSDate(timeIntervalSinceNow: $0)}
74+
return wait(until)
75+
}
76+
77+
/// Performs the signal operation on this semaphore
78+
public func signal() -> Int {
79+
underlyingSemaphore.lock()
80+
defer {
81+
underlyingSemaphore.unlock()
82+
}
83+
value += 1
84+
underlyingSemaphore.signal()
85+
return value
86+
}
87+
}

Tests/ExecutionContext/ExecutionContextTests.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ExecutionContextTests: XCTestCase {
3737
let expectation = self.expectationWithDescription("OK ASYNC")
3838

3939
context.async {
40-
sleep(1)
40+
sleep(1.0)
4141
expectation.fulfill()
4242
}
4343

@@ -61,11 +61,11 @@ class ExecutionContextTests: XCTestCase {
6161
ok = false
6262
}
6363

64-
sleep(2)
64+
sleep(2.0)
6565

6666
XCTAssert(ok)
6767

68-
sleep(2)
68+
sleep(2.0)
6969

7070
XCTAssertFalse(ok)
7171
}
@@ -134,6 +134,20 @@ class ExecutionContextTests: XCTestCase {
134134
afterTest(context)
135135
//afterTestAdvanced - no it will not work here
136136
}
137+
138+
func testSemaphore() {
139+
let sema = Semaphore()
140+
var n = 0
141+
for _ in [0...100] {
142+
global.execute {
143+
sema.wait()
144+
n += 1
145+
sleep(0.1)
146+
n -= 1
147+
sema.signal()
148+
}
149+
}
150+
}
137151
}
138152

139153
#if os(Linux)

0 commit comments

Comments
 (0)