@@ -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 }
0 commit comments