Skip to content

Commit 433226a

Browse files
committed
Port in concurrent classes from Uber internal repo
0 parents  commit 433226a

File tree

12 files changed

+1092
-0
lines changed

12 files changed

+1092
-0
lines changed

.gitignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Xcode
2+
*.xcodeproj
3+
*.xcworkspace
4+
5+
## Build generated
6+
build/
7+
DerivedData/
8+
9+
## Various settings
10+
*.pbxuser
11+
!default.pbxuser
12+
*.mode1v3
13+
!default.mode1v3
14+
*.mode2v3
15+
!default.mode2v3
16+
*.perspectivev3
17+
!default.perspectivev3
18+
xcuserdata/
19+
20+
## Other
21+
*.moved-aside
22+
*.xccheckout
23+
*.xcscmblueprint
24+
25+
## Obj-C/Swift specific
26+
*.hmap
27+
*.ipa
28+
*.dSYM.zip
29+
*.dSYM
30+
31+
# Swift Package Manager
32+
#
33+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
34+
Packages/
35+
Package.pins
36+
Package.resolved
37+
.build/

Package.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// Copyright (c) 2018. Uber Technologies
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+
// swift-tools-version:4.0
18+
// The swift-tools-version declares the minimum version of Swift required to build this package.
19+
20+
import PackageDescription
21+
22+
let package = Package(
23+
name: "Concurrency",
24+
products: [
25+
.library(
26+
name: "Concurrency",
27+
targets: ["Concurrency"]),
28+
],
29+
dependencies: [],
30+
targets: [
31+
.target(
32+
name: "Concurrency",
33+
dependencies: []),
34+
.testTarget(
35+
name: "ConcurrencyTests",
36+
dependencies: ["Concurrency"]),
37+
],
38+
swiftLanguageVersions: [4]
39+
)

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Swift Concurrency Utility Classes
2+
3+
A set of concurrency utility classes used by Uber. These are largely inspired by the equivalent (java.util.concurrent)[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html] package classes.
4+
5+
## `AtomicBool` provides locking-free synchronization of a mutable `Bool`. It provides higher performance than using locks to ensure thread-safety and synchronization correctness.
6+
7+
## `AtomicInt` provides locking-free synchronization of a mutable `Int`. It provides higher performance than using locks to ensure thread-safety and synchronization correctness.
8+
9+
## `AtomicReference` provides locking-free synchronization of a mutable object reference. It provides higher performance than using locks to ensure thread-safety and synchronization correctness.
10+
11+
## `CountDownLatch` is a utility class that allows coordination between threads. A count down latch starts with an initial count. Threads can then decrement the count until it reaches zero, at which point, the suspended waiting thread shall proceed.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// Copyright (c) 2018. Uber Technologies
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+
/// A concurrency utility class that supports locking-free synchronization on mutating an boolean
20+
/// value. Unlike using a lock, concurrent read and write accesses to this class is allowed. At
21+
/// the same time, concurrent operations using the atomic functions provided by this class ensures
22+
/// synchronization correctness without the higher cost of locking.
23+
public class AtomicBool {
24+
25+
/// The value that guarantees atomic read and write-through memory behavior.
26+
public var value: Bool {
27+
get {
28+
return AtomicBool.intToBool(value: backingAtomic.value)
29+
}
30+
set {
31+
let intValue = AtomicBool.boolToInt(value: newValue)
32+
backingAtomic.value = intValue
33+
}
34+
}
35+
36+
/// Initializer.
37+
///
38+
/// - parameter initialValue: The initial value.
39+
public init(initialValue: Bool) {
40+
let intValue = AtomicBool.boolToInt(value: initialValue)
41+
backingAtomic = AtomicInt(initialValue: intValue)
42+
}
43+
44+
/// Atomically sets the new value, if the current value equals the expected value.
45+
///
46+
/// - parameter expect: The expected value to compare against.
47+
/// - parameter newValue: The new value to set to if the comparison succeeds.
48+
/// - returns: true if the comparison succeeded and the value is set. false otherwise.
49+
@discardableResult
50+
public func compareAndSet(expect: Bool, newValue: Bool) -> Bool {
51+
let expectInt = AtomicBool.boolToInt(value: expect)
52+
let newValueInt = AtomicBool.boolToInt(value: newValue)
53+
return backingAtomic.compareAndSet(expect: expectInt, newValue: newValueInt)
54+
}
55+
56+
/// Atomically sets to the given new value and returns the old value.
57+
///
58+
/// - parameter newValue: The new value to set to.
59+
/// - returns: The old value.
60+
public func getAndSet(newValue: Bool) -> Bool {
61+
let newValueInt = AtomicBool.boolToInt(value: newValue)
62+
let resultIntValue = backingAtomic.getAndSet(newValue: newValueInt)
63+
return AtomicBool.intToBool(value: resultIntValue)
64+
}
65+
66+
// MARK: - Private
67+
68+
private static let falseIntValue = 0
69+
private static let trueIntValue = 1
70+
private let backingAtomic: AtomicInt
71+
72+
private static func boolToInt(value: Bool) -> Int {
73+
return value ? AtomicBool.trueIntValue : AtomicBool.falseIntValue
74+
}
75+
76+
private static func intToBool(value: Int) -> Bool {
77+
return (value == AtomicBool.trueIntValue) ? true : false
78+
}
79+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//
2+
// Copyright (c) 2018. Uber Technologies
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+
import libkern
19+
20+
/// A concurrency utility class that supports locking-free synchronization on mutating an integer
21+
/// value. Unlike using a lock, concurrent read and write accesses to this class is allowed. At
22+
/// the same time, concurrent operations using the atomic functions provided by this class ensures
23+
/// synchronization correctness without the higher cost of locking.
24+
public class AtomicInt {
25+
26+
/// The current value.
27+
public var value: Int {
28+
get {
29+
// Create a memory barrier to ensure the entire memory stack is in sync so we
30+
// can safely retrieve the value. This guarantees the initial value is in sync.
31+
OSMemoryBarrier()
32+
return Int(wrappedValue)
33+
}
34+
set {
35+
while true {
36+
let oldValue = self.value
37+
if self.compareAndSet(expect: oldValue, newValue: newValue) {
38+
break
39+
}
40+
}
41+
}
42+
}
43+
44+
/// Initializer.
45+
///
46+
/// - parameter initialValue: The initial value.
47+
public init(initialValue: Int) {
48+
wrappedValue = Int64(initialValue)
49+
}
50+
51+
/// Atomically sets the new value, if the current value equals the expected value.
52+
///
53+
/// - parameter expect: The expected value to compare against.
54+
/// - parameter newValue: The new value to set to if the comparison succeeds.
55+
/// - returns: true if the comparison succeeded and the value is set. false otherwise.
56+
@discardableResult
57+
public func compareAndSet(expect: Int, newValue: Int) -> Bool {
58+
return OSAtomicCompareAndSwap64Barrier(Int64(expect), Int64(newValue), &wrappedValue)
59+
}
60+
61+
/// Atomically increment the value and retrieve the new value.
62+
///
63+
/// - returns: The new value after incrementing.
64+
@discardableResult
65+
public func incrementAndGet() -> Int {
66+
let result = OSAtomicIncrement64Barrier(&wrappedValue)
67+
return Int(result)
68+
}
69+
70+
/// Atomically decrement the value and retrieve the new value.
71+
///
72+
/// - returns: The new value after decrementing.
73+
@discardableResult
74+
public func decrementAndGet() -> Int {
75+
let result = OSAtomicDecrement64Barrier(&wrappedValue)
76+
return Int(result)
77+
}
78+
79+
/// Atomically increment the value and retrieve the old value.
80+
///
81+
/// - returns: The old value before incrementing.
82+
@discardableResult
83+
public func getAndIncrement() -> Int {
84+
while true {
85+
let oldValue = self.value
86+
let newValue = oldValue + 1
87+
if self.compareAndSet(expect: oldValue, newValue: newValue) {
88+
return oldValue
89+
}
90+
}
91+
}
92+
93+
/// Atomically decrement the value and retrieve the old value.
94+
///
95+
/// - returns: The old value before decrementing.
96+
@discardableResult
97+
public func getAndDecrement() -> Int {
98+
while true {
99+
let oldValue = self.value
100+
let newValue = oldValue - 1
101+
if self.compareAndSet(expect: oldValue, newValue: newValue) {
102+
return oldValue
103+
}
104+
}
105+
}
106+
107+
/// Atomically sets to the given new value and returns the old value.
108+
///
109+
/// - parameter newValue: The new value to set to.
110+
/// - returns: The old value.
111+
@discardableResult
112+
public func getAndSet(newValue: Int) -> Int {
113+
while true {
114+
let oldValue = self.value
115+
if compareAndSet(expect: oldValue, newValue: newValue) {
116+
return oldValue
117+
}
118+
}
119+
}
120+
121+
// MARK: - Private
122+
123+
private var wrappedValue: Int64
124+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//
2+
// Copyright (c) 2018. Uber Technologies
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+
import libkern
19+
20+
/// A concurrency utility class that supports locking-free synchronization on mutating an object
21+
/// reference. Unlike using a lock, concurrent read and write accesses to this class is allowed. At
22+
/// the same time, concurrent operations using the atomic functions provided by this class ensures
23+
/// synchronization correctness without the higher cost of locking.
24+
public class AtomicReference<ValueType> {
25+
26+
/// The value that guarantees atomic read and write-through memory behavior.
27+
public var value: ValueType {
28+
get {
29+
// Create a memory barrier to ensure the entire memory stack is in sync so we
30+
// can safely retrieve the value. This guarantees the initial value is in sync.
31+
OSMemoryBarrier()
32+
return wrappedValue
33+
}
34+
set {
35+
while true {
36+
let oldValue = self.value
37+
if self.compareAndSet(expect: oldValue, newValue: newValue) {
38+
break
39+
}
40+
}
41+
}
42+
}
43+
44+
/// Initializer.
45+
///
46+
/// - parameter initialValue: The initial value.
47+
public init(initialValue: ValueType) {
48+
wrappedValue = initialValue
49+
pointer.pointee = unsafePassUnretainedPointer(value: wrappedValue)
50+
}
51+
52+
/// Atomically sets the new value, if the current value's memory pointer equals the
53+
/// expected value's memory pointer.
54+
///
55+
/// - parameter expect: The expected value to compare against.
56+
/// - parameter newValue: The new value to set to if the comparison succeeds.
57+
/// - returns: true if the comparison succeeded and the value is set. false otherwise.
58+
public func compareAndSet(expect: ValueType, newValue: ValueType) -> Bool {
59+
let expectPointer = unsafePassUnretainedPointer(value: expect)
60+
let newValuePointer = unsafePassUnretainedPointer(value: newValue)
61+
62+
if OSAtomicCompareAndSwapPtrBarrier(expectPointer, newValuePointer, pointer) {
63+
// If pointer swap succeeded, a memory berrier is created, so we can safely write the new
64+
// value.
65+
wrappedValue = newValue
66+
return true
67+
} else {
68+
return false
69+
}
70+
}
71+
72+
/// Atomically sets to the given new value and returns the old value.
73+
///
74+
/// - parameter newValue: The new value to set to.
75+
/// - returns: The old value.
76+
public func getAndSet(newValue: ValueType) -> ValueType {
77+
while true {
78+
let oldValue = self.value
79+
if compareAndSet(expect: oldValue, newValue: newValue) {
80+
return oldValue
81+
}
82+
}
83+
}
84+
85+
// MARK: - Private
86+
87+
private let pointer: UnsafeMutablePointer<UnsafeMutableRawPointer?> = UnsafeMutablePointer.allocate(capacity: 1)
88+
89+
private var wrappedValue: ValueType
90+
91+
private func unsafePassUnretainedPointer(value: ValueType) -> UnsafeMutableRawPointer {
92+
return UnsafeMutableRawPointer(Unmanaged.passUnretained(value as AnyObject).toOpaque())
93+
}
94+
95+
deinit {
96+
#if swift(>=4.1)
97+
pointer.deallocate()
98+
#else
99+
pointer.deallocate(capacity: 1)
100+
#endif
101+
}
102+
}

0 commit comments

Comments
 (0)