Skip to content

Commit 696c4fa

Browse files
committed
Improve Signal state management to avoid an ARC race.
1 parent d6268d3 commit 696c4fa

File tree

2 files changed

+278
-192
lines changed

2 files changed

+278
-192
lines changed

Sources/Atomic.swift

Lines changed: 87 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,65 +14,118 @@ import MachO
1414
/// A simple, generic lock-free finite state machine.
1515
///
1616
/// - warning: `deinitialize` must be called to dispose of the consumed memory.
17-
internal struct UnsafeAtomicState<State: RawRepresentable> where State.RawValue == Int32 {
18-
internal typealias Transition = (expected: State, next: State)
19-
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
20-
private let value: UnsafeMutablePointer<Int32>
17+
internal struct UnsafeAtomicInt32 {
18+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
19+
private let _value: UnsafeMutablePointer<Int32>
20+
21+
internal var value: Int32 {
22+
return _value.pointee
23+
}
2124

2225
/// Create a finite state machine with the specified initial state.
2326
///
2427
/// - parameters:
2528
/// - initial: The desired initial state.
26-
internal init(_ initial: State) {
27-
value = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
28-
value.initialize(to: initial.rawValue)
29+
internal init(_ initial: Int32) {
30+
_value = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
31+
_value.initialize(to: initial)
2932
}
3033

3134
/// Deinitialize the finite state machine.
3235
internal func deinitialize() {
33-
value.deinitialize()
34-
value.deallocate(capacity: 1)
36+
_value.deinitialize()
37+
_value.deallocate(capacity: 1)
3538
}
3639

37-
/// Compare the current state with the specified state.
40+
internal func swap(from expected: Int32, to next: Int32) -> Bool {
41+
return OSAtomicCompareAndSwap32Barrier(expected, next, _value)
42+
}
43+
44+
/// Increment the atomic integer.
3845
///
39-
/// - parameters:
40-
/// - expected: The expected state.
46+
/// - returns: The new value.
47+
@discardableResult
48+
internal func increment() -> Int32 {
49+
return OSAtomicIncrement32Barrier(_value)
50+
}
51+
52+
/// Decrement the atomic integer.
4153
///
42-
/// - returns: `true` if the current state matches the expected state.
43-
/// `false` otherwise.
44-
internal func `is`(_ expected: State) -> Bool {
45-
return OSAtomicCompareAndSwap32Barrier(expected.rawValue,
46-
expected.rawValue,
47-
value)
54+
/// - returns: The new value.
55+
@discardableResult
56+
internal func decrement() -> Int32 {
57+
return OSAtomicDecrement32Barrier(_value)
4858
}
59+
#else
60+
private let _value: Atomic<Int32>
4961

50-
/// Try to transition from the expected current state to the specified next
51-
/// state.
62+
private var value: Int32 {
63+
return _value.pointee
64+
}
65+
66+
/// Create a finite state machine with the specified initial state.
5267
///
5368
/// - parameters:
54-
/// - expected: The expected state.
55-
/// - next: The state to transition to.
69+
/// - initial: The desired initial state.
70+
internal init(_ initial: Int32) {
71+
_value = Atomic(initial)
72+
}
73+
74+
/// Deinitialize the finite state machine.
75+
internal func deinitialize() {}
76+
77+
internal func swap(from expected: Int32, to next: Int32) -> Bool {
78+
return _value.modify { value in
79+
guard value == expected else { return false }
80+
value = next
81+
return true
82+
}
83+
}
84+
85+
/// Increment the atomic integer.
5686
///
57-
/// - returns: `true` if the transition succeeds. `false` otherwise.
58-
internal func tryTransition(from expected: State, to next: State) -> Bool {
59-
return OSAtomicCompareAndSwap32Barrier(expected.rawValue,
60-
next.rawValue,
61-
value)
87+
/// - returns: The new value.
88+
@discardableResult
89+
internal func increment() -> Int32 {
90+
return _value.modify { value in
91+
value += 1
92+
return value
93+
}
6294
}
63-
#else
64-
private let value: Atomic<Int32>
95+
96+
/// Decrement the atomic integer.
97+
///
98+
/// - returns: The new value.
99+
@discardableResult
100+
internal func decrement() -> Int32 {
101+
return _value.modify { value in
102+
value -= 1
103+
return value
104+
}
105+
}
106+
#endif
107+
}
108+
109+
/// A simple, generic lock-free finite state machine.
110+
///
111+
/// - warning: `deinitialize` must be called to dispose of the consumed memory.
112+
internal struct UnsafeAtomicState<State: RawRepresentable> where State.RawValue == Int32 {
113+
internal typealias Transition = (expected: State, next: State)
114+
115+
private let value: UnsafeAtomicInt32
65116

66117
/// Create a finite state machine with the specified initial state.
67118
///
68119
/// - parameters:
69120
/// - initial: The desired initial state.
70121
internal init(_ initial: State) {
71-
value = Atomic(initial.rawValue)
122+
value = UnsafeAtomicInt32(initial.rawValue)
72123
}
73124

74125
/// Deinitialize the finite state machine.
75-
internal func deinitialize() {}
126+
internal func deinitialize() {
127+
value.deinitialize()
128+
}
76129

77130
/// Compare the current state with the specified state.
78131
///
@@ -82,26 +135,20 @@ internal struct UnsafeAtomicState<State: RawRepresentable> where State.RawValue
82135
/// - returns: `true` if the current state matches the expected state.
83136
/// `false` otherwise.
84137
internal func `is`(_ expected: State) -> Bool {
85-
return value.modify { $0 == expected.rawValue }
138+
return value.swap(from: expected.rawValue, to: expected.rawValue)
86139
}
87140

88141
/// Try to transition from the expected current state to the specified next
89142
/// state.
90143
///
91144
/// - parameters:
92145
/// - expected: The expected state.
146+
/// - next: The state to transition to.
93147
///
94148
/// - returns: `true` if the transition succeeds. `false` otherwise.
95149
internal func tryTransition(from expected: State, to next: State) -> Bool {
96-
return value.modify { value in
97-
if value == expected.rawValue {
98-
value = next.rawValue
99-
return true
100-
}
101-
return false
102-
}
150+
return value.swap(from: expected.rawValue, to: next.rawValue)
103151
}
104-
#endif
105152
}
106153

107154
/// `Lock` exposes `os_unfair_lock` on supported platforms, with pthread mutex as the

0 commit comments

Comments
 (0)