Skip to content

Commit 7bbb458

Browse files
authored
Merge pull request #342 from ReactiveCocoa/unfair-lock
Unfair Lock, take 2
2 parents bebb055 + ce743e4 commit 7bbb458

File tree

2 files changed

+87
-26
lines changed

2 files changed

+87
-26
lines changed

Sources/Atomic.swift

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,33 +104,97 @@ internal struct UnsafeAtomicState<State: RawRepresentable> where State.RawValue
104104
#endif
105105
}
106106

107-
final class PosixThreadMutex: NSLocking {
108-
private var mutex = pthread_mutex_t()
107+
/// `Lock` exposes `os_unfair_lock` on supported platforms, with pthread mutex as the
108+
// fallback.
109+
internal class Lock {
110+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
111+
@available(iOS 10.0, *)
112+
@available(macOS 10.12, *)
113+
@available(tvOS 10.0, *)
114+
@available(watchOS 3.0, *)
115+
internal final class UnfairLock: Lock {
116+
private var _lock: os_unfair_lock
117+
118+
override init() {
119+
_lock = os_unfair_lock()
120+
super.init()
121+
}
109122

110-
init() {
111-
let result = pthread_mutex_init(&mutex, nil)
112-
precondition(result == 0, "Failed to initialize mutex with error \(result).")
113-
}
123+
override func lock() {
124+
withUnsafeMutablePointer(to: &_lock, os_unfair_lock_lock)
125+
}
114126

115-
deinit {
116-
let result = pthread_mutex_destroy(&mutex)
117-
precondition(result == 0, "Failed to destroy mutex with error \(result).")
127+
override func unlock() {
128+
withUnsafeMutablePointer(to: &_lock, os_unfair_lock_unlock)
129+
}
130+
131+
override func `try`() -> Bool {
132+
return withUnsafeMutablePointer(to: &_lock, os_unfair_lock_trylock)
133+
}
118134
}
135+
#endif
136+
137+
internal final class PthreadLock: Lock {
138+
private var _lock: pthread_mutex_t
139+
140+
override init() {
141+
_lock = pthread_mutex_t()
142+
143+
let status = withUnsafeMutablePointer(to: &_lock) { pthread_mutex_init($0, nil) }
144+
assert(status == 0, "Unexpected pthread mutex error code: \(status)")
145+
146+
super.init()
147+
}
148+
149+
override func lock() {
150+
let status = withUnsafeMutablePointer(to: &_lock, pthread_mutex_lock)
151+
assert(status == 0, "Unexpected pthread mutex error code: \(status)")
152+
}
153+
154+
override func unlock() {
155+
let status = withUnsafeMutablePointer(to: &_lock, pthread_mutex_unlock)
156+
assert(status == 0, "Unexpected pthread mutex error code: \(status)")
157+
}
158+
159+
override func `try`() -> Bool {
160+
let status = withUnsafeMutablePointer(to: &_lock, pthread_mutex_trylock)
161+
switch status {
162+
case 0:
163+
return true
164+
case EBUSY:
165+
return false
166+
default:
167+
assertionFailure("Unexpected pthread mutex error code: \(status)")
168+
return false
169+
}
170+
}
119171

120-
func lock() {
121-
let result = pthread_mutex_lock(&mutex)
122-
precondition(result == 0, "Failed to lock \(self) with error \(result).")
172+
deinit {
173+
let status = withUnsafeMutablePointer(to: &_lock, pthread_mutex_destroy)
174+
assert(status == 0, "Unexpected pthread mutex error code: \(status)")
175+
}
123176
}
124177

125-
func unlock() {
126-
let result = pthread_mutex_unlock(&mutex)
127-
precondition(result == 0, "Failed to unlock \(self) with error \(result).")
178+
static func make() -> Lock {
179+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
180+
if #available(*, iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0) {
181+
return UnfairLock()
182+
}
183+
#endif
184+
185+
return PthreadLock()
128186
}
187+
188+
private init() {}
189+
190+
func lock() { fatalError() }
191+
func unlock() { fatalError() }
192+
func `try`() -> Bool { fatalError() }
129193
}
130194

131195
/// An atomic variable.
132196
public final class Atomic<Value> {
133-
private let lock: PosixThreadMutex
197+
private let lock: Lock
134198
private var _value: Value
135199

136200
/// Atomically get or set the value of the variable.
@@ -150,7 +214,7 @@ public final class Atomic<Value> {
150214
/// - value: Initial value for `self`.
151215
public init(_ value: Value) {
152216
_value = value
153-
lock = PosixThreadMutex()
217+
lock = Lock.make()
154218
}
155219

156220
/// Atomically modifies the variable.

Sources/Signal.swift

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ public final class Signal<Value, Error: Swift.Error> {
4040
private var state: SignalState<Value, Error>
4141

4242
/// Used to ensure that state updates are serialized.
43-
private let updateLock: NSLock
43+
private let updateLock: Lock
4444

4545
/// Used to ensure that events are serialized during delivery to observers.
46-
private let sendLock: NSLock
46+
private let sendLock: Lock
4747

4848
/// Initialize a Signal that will immediately invoke the given generator,
4949
/// then forward events sent to the given observer.
@@ -57,10 +57,8 @@ public final class Signal<Value, Error: Swift.Error> {
5757
/// that will act as an event emitter for the signal.
5858
public init(_ generator: (Observer) -> Disposable?) {
5959
state = .alive(AliveState())
60-
updateLock = NSLock()
61-
updateLock.name = "org.reactivecocoa.ReactiveSwift.Signal.updateLock"
62-
sendLock = NSLock()
63-
sendLock.name = "org.reactivecocoa.ReactiveSwift.Signal.sendLock"
60+
updateLock = Lock.make()
61+
sendLock = Lock.make()
6462

6563
let observer = Observer { [weak self] event in
6664
guard let signal = self else {
@@ -893,7 +891,7 @@ private final class CombineLatestState<Value> {
893891
}
894892

895893
extension Signal {
896-
private func observeWithStates<U>(_ signalState: CombineLatestState<Value>, _ otherState: CombineLatestState<U>, _ lock: NSLock, _ observer: Signal<(), Error>.Observer) -> Disposable? {
894+
private func observeWithStates<U>(_ signalState: CombineLatestState<Value>, _ otherState: CombineLatestState<U>, _ lock: Lock, _ observer: Signal<(), Error>.Observer) -> Disposable? {
897895
return self.observe { event in
898896
switch event {
899897
case let .value(value):
@@ -944,8 +942,7 @@ extension Signal {
944942
/// and given signal.
945943
public func combineLatest<U>(with other: Signal<U, Error>) -> Signal<(Value, U), Error> {
946944
return Signal<(Value, U), Error> { observer in
947-
let lock = NSLock()
948-
lock.name = "org.reactivecocoa.ReactiveSwift.combineLatestWith"
945+
let lock = Lock.make()
949946

950947
let signalState = CombineLatestState<Value>()
951948
let otherState = CombineLatestState<U>()

0 commit comments

Comments
 (0)