Skip to content

Commit cdc8bc7

Browse files
committed
Refactoring
1 parent 578e108 commit cdc8bc7

File tree

6 files changed

+39
-47
lines changed

6 files changed

+39
-47
lines changed

Sources/Atoms/Core/StoreContext.swift

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ internal struct StoreContext {
127127
let scopeKey = lookupScopeKey(of: atom, isScopedOverriden: override?.isScoped ?? false)
128128
let key = AtomKey(atom, scopeKey: scopeKey)
129129
let cache = getCache(of: atom, for: key, override: override)
130-
let isNewSubscription = subscriber.subscribingKeys.insert(key).inserted
130+
let isNewSubscription = subscriber.subscribing.insert(key).inserted
131131

132-
store.state.subscriptions[key, default: [:]].updateValue(subscription, forKey: subscriber.key)
132+
store.state.subscriptions[key, default: [:]][subscriber.key] = subscription
133133
subscriber.unsubscribe = { keys in
134134
unsubscribe(keys, for: subscriber.key)
135135
}
@@ -233,7 +233,7 @@ internal struct StoreContext {
233233
let scopeKey = lookupScopeKey(of: atom, isScopedOverriden: override?.isScoped ?? false)
234234
let key = AtomKey(atom, scopeKey: scopeKey)
235235

236-
subscriber.subscribingKeys.remove(key)
236+
subscriber.subscribing.remove(key)
237237
unsubscribe([key], for: subscriber.key)
238238
}
239239

@@ -355,32 +355,27 @@ private extension StoreContext {
355355
var skippedDependencies = Set<AtomKey>()
356356

357357
// Updates the given atom.
358-
func update(atom: some Atom, for key: AtomKey) {
359-
guard let cache = lookupCache(of: atom, for: key) else {
360-
// Here is usually unreachable.
361-
return
362-
}
363-
364-
let override = lookupOverride(of: atom)
365-
let newCache = makeCache(of: atom, for: key, override: override)
358+
func update(for key: AtomKey, cache: some AtomCacheProtocol) {
359+
let override = lookupOverride(of: cache.atom)
360+
let newCache = makeCache(of: cache.atom, for: key, override: override)
366361

367362
// Check whether if the dependent atoms should be updated transitively.
368-
guard atom._loader.shouldUpdateTransitively(newValue: newCache.value, oldValue: cache.value) else {
363+
guard cache.atom._loader.shouldUpdateTransitively(newValue: newCache.value, oldValue: cache.value) else {
369364
// Record the atom to avoid downstream from being update.
370365
skippedDependencies.insert(key)
371366
return
372367
}
373368

374369
// Perform side effects before updating downstream.
375-
let state = getState(of: atom, for: key)
370+
let state = getState(of: cache.atom, for: key)
376371
let context = AtomCurrentContext(store: self, coordinator: state.coordinator)
377-
atom.updated(newValue: newCache.value, oldValue: cache.value, context: context)
372+
cache.atom.updated(newValue: newCache.value, oldValue: cache.value, context: context)
378373
}
379374

380375
// Performs update of the given atom with the dependency's context.
381-
func performUpdate(atom: some Atom, for key: AtomKey, dependency: some Atom) {
376+
func performUpdate(for key: AtomKey, cache: some AtomCacheProtocol, dependency: some Atom) {
382377
dependency._loader.performTransitiveUpdate {
383-
update(atom: atom, for: key)
378+
update(for: key, cache: cache)
384379
}
385380
}
386381

@@ -389,28 +384,19 @@ private extension StoreContext {
389384
dependency._loader.performTransitiveUpdate(subscription.update)
390385
}
391386

392-
// Do not transitively update atoms that have dependency recorded not to update downstream.
393-
// However, if the topological sorting has already skipped the vertex as a redundant,
394-
// it should be performed.
395387
func convertToValidEdge(_ edge: Edge) -> Edge? {
388+
// Do not transitively update atoms that have dependency recorded not to update downstream.
396389
guard skippedDependencies.contains(edge.from) else {
397390
return edge
398391
}
399392

400-
guard let redundantDependencies = redundants[edge.to] else {
393+
// If the topological sorting has marked the vertex as a redundant, the update still performed.
394+
guard let fromKey = redundants[edge.to]?.first(where: { !skippedDependencies.contains($0) }) else {
401395
return nil
402396
}
403397

404-
// Topological sorting itself does not guarantee idempotent result when multiple
405-
// dependencies update simultaneously and there's no valid update order to determine
406-
// which atom triggered the transitive update, and thus here chooses a random
407-
// dependency atom from redundant edges.
408-
guard let fromKey = redundantDependencies.subtracting(skippedDependencies).first else {
409-
return nil
410-
}
411-
412-
// Convert edge's `from`, which represents a dependency atom, to a non-skipped one on
413-
// a best-effort basis to switch the update transaction context (e.g. animation).
398+
// Convert edge's `from`, which represents a dependency atom, to a non-skipped one to
399+
// change the update transaction context (e.g. animation).
414400
return Edge(from: fromKey, to: edge.to)
415401
}
416402

@@ -424,16 +410,22 @@ private extension StoreContext {
424410
continue
425411
}
426412

427-
if let cache = store.state.caches[key], let dependencyCache = store.state.caches[edge.from] {
428-
performUpdate(atom: cache.atom, for: key, dependency: dependencyCache.atom)
413+
let cache = store.state.caches[key]
414+
let dependencyCache = store.state.caches[edge.from]
415+
416+
if let cache, let dependencyCache {
417+
performUpdate(for: key, cache: cache, dependency: dependencyCache.atom)
429418
}
430419

431420
case .subscriber(let key):
432421
guard let edge = convertToValidEdge(edge) else {
433422
continue
434423
}
435424

436-
if let subscription = store.state.subscriptions[edge.from]?[key], let dependencyCache = store.state.caches[edge.from] {
425+
let subscription = store.state.subscriptions[edge.from]?[key]
426+
let dependencyCache = store.state.caches[edge.from]
427+
428+
if let subscription, let dependencyCache {
437429
performUpdate(subscription: subscription, dependency: dependencyCache.atom)
438430
}
439431
}

Sources/Atoms/Core/Subscriber.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ internal struct Subscriber {
1010
self.key = SubscriberKey(token: state.token)
1111
}
1212

13-
var subscribingKeys: Set<AtomKey> {
14-
get { state?.subscribingKeys ?? [] }
15-
nonmutating set { state?.subscribingKeys = newValue }
13+
var subscribing: Set<AtomKey> {
14+
get { state?.subscribing ?? [] }
15+
nonmutating set { state?.subscribing = newValue }
1616
}
1717

1818
var unsubscribe: ((Set<AtomKey>) -> Void)? {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
@MainActor
22
final class SubscriberState {
33
let token = SubscriberKey.Token()
4-
var subscribingKeys = Set<AtomKey>()
4+
var subscribing = Set<AtomKey>()
55
var unsubscribe: ((Set<AtomKey>) -> Void)?
66

77
init() {}
88

99
deinit {
10-
unsubscribe?(subscribingKeys)
10+
unsubscribe?(subscribing)
1111
}
1212
}

Sources/Atoms/Core/TopologicalSort.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ internal struct Edge: Hashable {
1212
@MainActor
1313
internal func topologicalSort(key: AtomKey, store: AtomStore) -> (
1414
edges: ReversedCollection<[Edge]>,
15-
redundants: [Vertex: Set<AtomKey>] // key = vertex, value = dependencies
15+
redundants: [Vertex: [AtomKey]] // key = vertex, value = dependencies
1616
) {
1717
var trace = Set<Vertex>()
1818
var edges = [Edge]()
19-
var redundants = [Vertex: Set<AtomKey>]()
19+
var redundants = [Vertex: [AtomKey]]()
2020

2121
func traverse(key: AtomKey, isRedundant: Bool) {
2222
if let children = store.graph.children[key] {
@@ -43,7 +43,7 @@ internal func topologicalSort(key: AtomKey, store: AtomStore) -> (
4343
traverse(key: key, isRedundant: isRedundant)
4444

4545
if isRedundant {
46-
redundants[vertex, default: []].insert(fromKey)
46+
redundants[vertex, default: []].append(fromKey)
4747
}
4848
else {
4949
let edge = Edge(from: fromKey, to: vertex)
@@ -58,7 +58,7 @@ internal func topologicalSort(key: AtomKey, store: AtomStore) -> (
5858
trace.insert(vertex)
5959

6060
if isRedundant {
61-
redundants[vertex, default: []].insert(fromKey)
61+
redundants[vertex, default: []].append(fromKey)
6262
}
6363
else {
6464
let edge = Edge(from: fromKey, to: vertex)

Tests/AtomsTests/Core/StoreContextTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ final class StoreContextTests: XCTestCase {
312312
)
313313

314314
XCTAssertEqual(initialValue, 0)
315-
XCTAssertTrue(subscriber.subscribingKeys.contains(key))
315+
XCTAssertTrue(subscriber.subscribing.contains(key))
316316
XCTAssertNotNil(store.state.subscriptions[key]?[subscriber.key])
317317
XCTAssertEqual((store.state.caches[key] as? AtomCache<TestAtom>)?.value, 0)
318318
XCTAssertEqual((store.state.caches[dependencyKey] as? AtomCache<DependencyAtom>)?.value, 0)

Tests/AtomsTests/Core/SubscriberTestsTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ final class SubscriberTests: XCTestCase {
2424
}
2525

2626
@MainActor
27-
func testSubscribingKeys() {
27+
func testSubscribing() {
2828
var state: SubscriberState? = SubscriberState()
2929
let subscriber = Subscriber(state!)
3030
let expected: Set = [
@@ -33,13 +33,13 @@ final class SubscriberTests: XCTestCase {
3333
AtomKey(TestAtom(value: 2)),
3434
]
3535

36-
subscriber.subscribingKeys = expected
36+
subscriber.subscribing = expected
3737

38-
XCTAssertEqual(state?.subscribingKeys, expected)
38+
XCTAssertEqual(state?.subscribing, expected)
3939

4040
state = nil
4141

42-
XCTAssertTrue(subscriber.subscribingKeys.isEmpty)
42+
XCTAssertTrue(subscriber.subscribing.isEmpty)
4343
}
4444

4545
@MainActor

0 commit comments

Comments
 (0)