Skip to content

Commit dfcd33a

Browse files
committed
Break SignalStateSnapshot into AliveState and TerminatingState.
1 parent 6f630c0 commit dfcd33a

File tree

1 file changed

+51
-49
lines changed

1 file changed

+51
-49
lines changed

Sources/Signal.swift

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public final class Signal<Value, Error: Swift.Error> {
5656
/// - generator: A closure that accepts an implicitly created observer
5757
/// that will act as an event emitter for the signal.
5858
public init(_ generator: (Observer) -> Disposable?) {
59-
state = .alive(SignalStateSnapshot())
59+
state = .alive(AliveState())
6060
updateLock = NSLock()
6161
updateLock.name = "org.reactivecocoa.ReactiveSwift.Signal.updateLock"
6262
sendLock = NSLock()
@@ -73,8 +73,7 @@ public final class Signal<Value, Error: Swift.Error> {
7373
//
7474
// Read directly.
7575
//
76-
// - Extract the snapshot of a signal that is alive, and deliver events
77-
// using the snapshot.
76+
// - Deliver `value` events in the `alive` state.
7877
//
7978
// `sendLock` must be acquired.
8079
//
@@ -83,12 +82,12 @@ public final class Signal<Value, Error: Swift.Error> {
8382
//
8483
// `updateLock` must be acquired.
8584
//
86-
// - Transit from `alive` to `terminating`.
85+
// - Transition from `alive` to `terminating`.
8786
//
8887
// `updateLock` must be acquired.
8988
//
90-
// - Extract the snapshot of a terminating signal to deliver the
91-
// termination event, and transit from `terminating` to `terminated`.
89+
// - Deliver the termination event in the `terminating` state, and
90+
// transition from `terminating` to `terminated`.
9291
//
9392
// Both `sendLock` and `updateLock` must be acquired. The state must
9493
// also be checked again after the locks are acquired. Fail gracefully
@@ -123,19 +122,18 @@ public final class Signal<Value, Error: Swift.Error> {
123122
// occasionally one of the senders waiting on `sendLock`.
124123
signal.updateLock.lock()
125124

126-
if case let .alive(snapshot) = signal.state {
127-
let newSnapshot = SignalStateSnapshot(observers: snapshot.observers,
128-
retaining: snapshot.retaining,
129-
associated: event)
125+
if case let .alive(state) = signal.state {
126+
let newSnapshot = TerminatingState(observers: state.observers,
127+
event: event)
130128
signal.state = .terminating(newSnapshot)
131129
signal.updateLock.unlock()
132130

133131
if signal.sendLock.try() {
134-
// Check whether the termination event has been handled by a
132+
// Check whether the terminating state has been handled by a
135133
// concurrent sender. If not, handle it.
136-
if let snapshot = signal.shouldReallyTerminate() {
137-
for observer in snapshot.observers {
138-
observer.action(snapshot.associated)
134+
if let terminatingState = signal.shouldReallyTerminate() {
135+
for observer in terminatingState.observers {
136+
observer.action(terminatingState.event)
139137
}
140138
}
141139

@@ -169,16 +167,16 @@ public final class Signal<Value, Error: Swift.Error> {
169167
signal.sendLock.lock()
170168
// Start of the main protected section.
171169

172-
if case let .alive(snapshot) = signal.state {
173-
for observer in snapshot.observers {
170+
if case let .alive(state) = signal.state {
171+
for observer in state.observers {
174172
observer.action(event)
175173
}
176174

177175
// Check if the status has been bumped to `terminating` due to a
178176
// concurrent or a recursive termination event.
179-
if case .terminating = signal.state, let snapshot = signal.shouldReallyTerminate() {
180-
for observer in snapshot.observers {
181-
observer.action(snapshot.associated)
177+
if case .terminating = signal.state, let terminatingState = signal.shouldReallyTerminate() {
178+
for observer in terminatingState.observers {
179+
observer.action(terminatingState.event)
182180
}
183181

184182
shouldDispose = true
@@ -194,9 +192,9 @@ public final class Signal<Value, Error: Swift.Error> {
194192
if !shouldDispose, case .terminating = signal.state {
195193
signal.sendLock.lock()
196194

197-
if let snapshot = signal.shouldReallyTerminate() {
198-
for observer in snapshot.observers {
199-
observer.action(snapshot.associated)
195+
if let terminatingState = signal.shouldReallyTerminate() {
196+
for observer in terminatingState.observers {
197+
observer.action(terminatingState.event)
200198
}
201199

202200
shouldDispose = true
@@ -311,7 +309,7 @@ public final class Signal<Value, Error: Swift.Error> {
311309
if case let .alive(snapshot) = state {
312310
var observers = snapshot.observers
313311
token = observers.insert(observer)
314-
state = .alive(SignalStateSnapshot(observers: observers, retaining: self))
312+
state = .alive(AliveState(observers: observers, retaining: self))
315313
}
316314
updateLock.unlock()
317315

@@ -322,8 +320,8 @@ public final class Signal<Value, Error: Swift.Error> {
322320
if case let .alive(snapshot) = s.state {
323321
var observers = snapshot.observers
324322
observers.remove(using: token)
325-
s.state = .alive(SignalStateSnapshot(observers: observers,
326-
retaining: observers.isEmpty ? nil : self))
323+
s.state = .alive(AliveState(observers: observers,
324+
retaining: observers.isEmpty ? nil : self))
327325
}
328326
s.updateLock.unlock()
329327
}
@@ -343,58 +341,62 @@ public final class Signal<Value, Error: Swift.Error> {
343341
/// The Swift compiler has also an optimization for enums with payloads that are
344342
/// all reference counted, and at most one no-payload case.
345343
private enum SignalState<Value, Error: Swift.Error> {
346-
// FIXME: Change `()?` to `()` when concrete same-type requirement lands.
347344
/// The `Signal` is alive.
348-
case alive(SignalStateSnapshot<Value, Error, ()?>)
345+
case alive(AliveState<Value, Error>)
349346

350347
/// The `Signal` has received a termination event, and is about to be
351348
/// terminated.
352-
case terminating(SignalStateSnapshot<Value, Error, Event<Value, Error>>)
349+
case terminating(TerminatingState<Value, Error>)
353350

354351
/// The `Signal` has terminated.
355352
case terminated
356353
}
357354

358-
/// The state snapshot of a `Signal` which contains the bag of observers,
359-
/// an optional self-retaining reference and an optional associated value.
360-
///
361-
/// As the amount of state would definitely span over a cache line,
362-
/// `SignalStateSnapshot` is set to be a reference type so that we can
363-
/// atomically update the reference instead.
364-
///
365-
/// - warning: In-place mutation should not be introduced to
366-
/// `SignalStateSnapshot`. Copy the states and create a new instance.
367-
private final class SignalStateSnapshot<Value, Error: Swift.Error, U> {
355+
// As the amount of state would definitely span over a cache line,
356+
// `AliveState` and `TerminatingState` is set to be a reference type so
357+
// that we can atomically update the reference instead.
358+
//
359+
// Note that in-place mutation should not be introduced to `AliveState` and
360+
// `TerminatingState`. Copy the states and create a new instance.
361+
362+
/// The state of a `Signal` that is alive. It contains a bag of observers and
363+
/// an optional self-retaining reference.
364+
private final class AliveState<Value, Error: Swift.Error> {
368365
/// The observers of the `Signal`.
369366
fileprivate let observers: Bag<Signal<Value, Error>.Observer>
370367

371368
/// A self-retaining reference. It is set when there are one or more active
372369
/// observers.
373370
fileprivate let retaining: Signal<Value, Error>?
374371

375-
fileprivate let associated: U
376-
377-
/// Create a snapshot.
372+
/// Create an alive state.
378373
///
379374
/// - parameters:
380375
/// - observers: The latest bag of observers.
381376
/// - retaining: The self-retaining reference of the `Signal`, if necessary.
382-
/// - associated: An associated value.
383-
init(observers: Bag<Signal<Value, Error>.Observer> = Bag(), retaining: Signal<Value, Error>? = nil, associated: U) {
377+
init(observers: Bag<Signal<Value, Error>.Observer> = Bag(), retaining: Signal<Value, Error>? = nil) {
384378
self.observers = observers
385379
self.retaining = retaining
386-
self.associated = associated
387380
}
388381
}
389382

390-
extension SignalStateSnapshot where U: ExpressibleByNilLiteral {
391-
/// Create a snapshot.
383+
/// The state of a terminating `Signal`. It contains a bag of observers and the
384+
/// termination event.
385+
private final class TerminatingState<Value, Error: Swift.Error> {
386+
/// The observers of the `Signal`.
387+
fileprivate let observers: Bag<Signal<Value, Error>.Observer>
388+
389+
/// The termination event.
390+
fileprivate let event: Event<Value, Error>
391+
392+
/// Create a terminating state.
392393
///
393394
/// - parameters:
394395
/// - observers: The latest bag of observers.
395-
/// - retaining: The self-retaining reference of the `Signal`, if necessary.
396-
convenience init(observers: Bag<Signal<Value, Error>.Observer> = Bag(), retaining: Signal<Value, Error>? = nil) {
397-
self.init(observers: observers, retaining: retaining, associated: nil)
396+
/// - event: The termination event.
397+
init(observers: Bag<Signal<Value, Error>.Observer>, event: Event<Value, Error>) {
398+
self.observers = observers
399+
self.event = event
398400
}
399401
}
400402

0 commit comments

Comments
 (0)