Loading packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt +1 −1 Original line number Diff line number Diff line Loading @@ -836,7 +836,7 @@ internal fun BuildScope.effect( nameData: NameData, context: CoroutineContext = EmptyCoroutineContext, block: EffectScope.() -> Unit, ): Job = launchScope(nameData + "launch", context) { now.observe(name = nameData) { block() } } ): Job = launchScope(nameData + "launchScope", context) { now.observe(name = nameData) { block() } } /** * Performs a side-effect in a safe manner w/r/t the current Kairos transaction. Loading packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt +14 −5 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import com.android.systemui.kairos.internal.util.invokeOnCancel import com.android.systemui.kairos.internal.util.launchImmediate import com.android.systemui.kairos.launchEffect import com.android.systemui.kairos.mergeLeft import com.android.systemui.kairos.skipNext import com.android.systemui.kairos.takeUntil import com.android.systemui.kairos.util.Maybe import com.android.systemui.kairos.util.Maybe.Absent Loading Loading @@ -219,7 +218,7 @@ internal class BuildScopeImpl( val newName = nameData.mapName { "$it[key=$k, epoch=$epoch, init = false]" } val newEnd: Events<Maybe<BuildSpec<A>>> = skipNext(newName + "newEnd", eventsByKey[k]) eventsByKey[k].skipNextUnsafe(newName + "newEnd") val newScope = childBuildScope(newEnd, newName) newScope.spec() } Loading Loading @@ -388,8 +387,18 @@ internal class BuildScopeImpl( val newCoroutineScope: CoroutineScope = coroutineScope.childScope() val newChildBuildScope = newChildBuildScope(newCoroutineScope, newEnd, nameData) // When the end signal emits, cancel all running coroutines in the new scope newChildBuildScope.deathSignal.observeSync(nameData + "observeLifetime") { newCoroutineScope.cancel() val outputNode = Output<Any>(nameData + "observeLifetime", onEmit = { newCoroutineScope.cancel() }) deferAction { newChildBuildScope.deathSignal.init .connect(stateScope.evalScope) .activate(stateScope.evalScope, outputNode.schedulable) ?.let { (conn, needsEval) -> outputNode.upstream = conn if (needsEval) { outputNode.schedule(0, evalScope = stateScope.evalScope) } } } return newChildBuildScope } Loading @@ -402,7 +411,7 @@ internal class BuildScopeImpl( val newCoroutineScope = coroutineScope.childScope(coroutineContext) // If the job is cancelled, emit the stop signal newCoroutineScope.coroutineContext.job.invokeOnCompletion { stopEmitter.emit(Unit) } return newChildBuildScope(newCoroutineScope, stopEmitter, nameData) return newChildBuildScope(newCoroutineScope, stopEmitter, childNameData) } private fun newChildBuildScope( Loading packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt +6 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.kairos.internal import androidx.collection.ScatterSet import com.android.systemui.kairos.internal.store.HashMapK import com.android.systemui.kairos.internal.store.MapHolder import com.android.systemui.kairos.internal.store.MapK Loading Loading @@ -76,7 +77,7 @@ internal class DemuxNode<W, K, A>( override fun moveIndirectUpstreamToDirect( scheduler: Scheduler, oldIndirectDepth: Int, oldIndirectSet: Set<MuxDeferredNode<*, *, *>>, oldIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, newDirectDepth: Int, ) { branchNodeByKey.forEach { _, branchNode -> Loading @@ -93,8 +94,8 @@ internal class DemuxNode<W, K, A>( scheduler: Scheduler, oldDepth: Int, newDepth: Int, removals: Set<MuxDeferredNode<*, *, *>>, additions: Set<MuxDeferredNode<*, *, *>>, removals: ScatterSet<MuxDeferredNode<*, *, *>>, additions: ScatterSet<MuxDeferredNode<*, *, *>>, ) { branchNodeByKey.forEach { _, branchNode -> branchNode.downstreamSet.adjustIndirectUpstream( Loading @@ -111,7 +112,7 @@ internal class DemuxNode<W, K, A>( scheduler: Scheduler, oldDirectDepth: Int, newIndirectDepth: Int, newIndirectSet: Set<MuxDeferredNode<*, *, *>>, newIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { branchNodeByKey.forEach { _, branchNode -> branchNode.downstreamSet.moveDirectUpstreamToIndirect( Loading @@ -126,7 +127,7 @@ internal class DemuxNode<W, K, A>( override fun removeIndirectUpstream( scheduler: Scheduler, depth: Int, indirectSet: Set<MuxDeferredNode<*, *, *>>, indirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { lifecycle.lifecycleState = DemuxLifecycleState.Dead branchNodeByKey.forEach { _, branchNode -> Loading packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt +96 −80 Original line number Diff line number Diff line Loading @@ -17,10 +17,14 @@ package com.android.systemui.kairos.internal import androidx.collection.MutableScatterSet import androidx.collection.ScatterSet import androidx.collection.mutableScatterSetOf import com.android.systemui.kairos.internal.util.Bag import com.android.systemui.kairos.internal.util.fastForEach import java.util.TreeMap internal val InputTracker = DepthTracker().apply { snapshotIsDirect = true } /** * Tracks all upstream connections for Mux nodes. * Loading Loading @@ -63,27 +67,29 @@ import java.util.TreeMap */ internal class DepthTracker { var snapshotIsDirect = true var snapshotIsDirect = false private var snapshotIsIndirectRoot = false private inline val snapshotIsIndirect: Boolean get() = !snapshotIsDirect var snapshotIndirectDepth: Int = 0 var snapshotDirectDepth: Int = 0 private val _snapshotIndirectRoots = HashSet<MuxDeferredNode<*, *, *>>() val snapshotIndirectRoots get() = _snapshotIndirectRoots.toSet() private val _snapshotIndirectRoots = MutableScatterSet<MuxDeferredNode<*, *, *>>() val snapshotIndirectRoots: ScatterSet<MuxDeferredNode<*, *, *>> get() = _snapshotIndirectRoots.toScatterSet() private val indirectAdditions = MutableScatterSet<MuxDeferredNode<*, *, *>>() private val indirectRemovals = MutableScatterSet<MuxDeferredNode<*, *, *>>() private inline val trackIndirectRootDiffs get() = !snapshotIsDirect // && !snapshotIsIndirectRoot private val indirectAdditions = HashSet<MuxDeferredNode<*, *, *>>() private val indirectRemovals = HashSet<MuxDeferredNode<*, *, *>>() private val dirty_directUpstreamDepths = TreeMap<Int, Int>() private val dirty_indirectUpstreamDepths = TreeMap<Int, Int>() private val dirty_indirectUpstreamRoots = Bag<MuxDeferredNode<*, *, *>>() var dirty_directDepth = 0 private var dirty_indirectDepth = 0 private var dirty_depthIsDirect = true private var dirty_depthIsDirect = false private var dirty_isIndirectRoot = false fun schedule(scheduler: Scheduler, node: MuxNode<*, *, *>) { Loading Loading @@ -161,32 +167,37 @@ internal class DepthTracker { } fun updateIndirectRoots( additions: Set<MuxDeferredNode<*, *, *>>? = null, removals: Set<MuxDeferredNode<*, *, *>>? = null, additions: ScatterSet<MuxDeferredNode<*, *, *>>? = null, removals: ScatterSet<MuxDeferredNode<*, *, *>>? = null, butNot: MuxDeferredNode<*, *, *>? = null, ): Boolean { val addsChanged = additions ?.let { dirty_indirectUpstreamRoots.addAll(additions, butNot) } ?.let { newlyAdded -> if (trackIndirectRootDiffs) { val remainder = indirectRemovals.applyRemovalDiff(newlyAdded) indirectAdditions.addAll(remainder) } true } ?: false val removalsChanged = removals ?.let { dirty_indirectUpstreamRoots.removeAll(removals) } ?.let { fullyRemoved -> if (trackIndirectRootDiffs) { val remainder = indirectAdditions.applyRemovalDiff(fullyRemoved) indirectRemovals.addAll(remainder) } true } ?: false return (!dirty_depthIsDirect && (addsChanged || removalsChanged)) } private fun <T> HashSet<T>.applyRemovalDiff(changeSet: HashSet<T>): Set<T> { val remainder = HashSet<T>() for (element in changeSet) { private fun <T> MutableScatterSet<T>.applyRemovalDiff(changeSet: ScatterSet<T>): ScatterSet<T> { if (isEmpty()) return changeSet val remainder = mutableScatterSetOf<T>() changeSet.forEach { element -> if (!remove(element)) { remainder.add(element) } Loading @@ -200,30 +211,32 @@ internal class DepthTracker { } } fun applyChanges( scheduler: Scheduler, downstreamSet: DownstreamSet, muxNode: MuxNode<*, *, *>, ) { private fun <T> buildScatterSet(block: MutableScatterSet<T>.() -> Unit): ScatterSet<T> = mutableScatterSetOf<T>().apply(block) private fun <T> ScatterSet<T>.toScatterSet(): ScatterSet<T> = buildScatterSet { addAll(this@toScatterSet) } fun applyChanges(scheduler: Scheduler, downstreamSet: DownstreamSet, owner: MuxNode<*, *, *>) { when { dirty_depthIsDirect -> { if (snapshotIsDirect) { val oldDepth = snapshotDirectDepth reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.adjustDirectUpstream( scheduler, oldDepth = snapshotDirectDepth, oldDepth = oldDepth, newDepth = dirty_directDepth, ) } else { val oldIndirectDepth = snapshotIndirectDepth val oldIndirectSet = snapshotIndirectRoots reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.moveIndirectUpstreamToDirect( scheduler, oldIndirectDepth = snapshotIndirectDepth, oldIndirectSet = buildSet { addAll(snapshotIndirectRoots) if (snapshotIsIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) } }, oldIndirectDepth = oldIndirectDepth, oldIndirectSet = oldIndirectSet, newDirectDepth = dirty_directDepth, ) } Loading @@ -231,44 +244,49 @@ internal class DepthTracker { dirty_hasIndirectUpstream() || dirty_isIndirectRoot -> { if (snapshotIsDirect) { val oldDirectDepth = snapshotDirectDepth val newIndirectSet = buildScatterSet { dirty_indirectUpstreamRoots.addAllKeysTo(this) if (dirty_isIndirectRoot) { add(owner as MuxDeferredNode<*, *, *>) } } reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.moveDirectUpstreamToIndirect( scheduler, oldDirectDepth = snapshotDirectDepth, oldDirectDepth = oldDirectDepth, newIndirectDepth = dirty_indirectDepth, newIndirectSet = buildSet { addAll(dirty_indirectUpstreamRoots) if (dirty_isIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) } }, newIndirectSet = newIndirectSet, ) } else { downstreamSet.adjustIndirectUpstream( scheduler, oldDepth = snapshotIndirectDepth, newDepth = dirty_indirectDepth, removals = buildSet { val oldDepth = snapshotIndirectDepth val wasIndirectRoot = snapshotIsIndirectRoot val removals = buildScatterSet { addAll(indirectRemovals) if (snapshotIsIndirectRoot && !dirty_isIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) if (wasIndirectRoot && !dirty_isIndirectRoot) { add(owner as MuxDeferredNode<*, *, *>) } }, additions = buildSet { } val additions = buildScatterSet { addAll(indirectAdditions) if (!snapshotIsIndirectRoot && dirty_isIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) if (!wasIndirectRoot && dirty_isIndirectRoot) { add(owner as MuxDeferredNode<*, *, *>) } } }, reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.adjustIndirectUpstream( scheduler, oldDepth = oldDepth, newDepth = dirty_indirectDepth, removals = removals, additions = additions, ) } } else -> { // die muxNode.lifecycle.lifecycleState = MuxLifecycleState.Dead owner.lifecycle.lifecycleState = MuxLifecycleState.Dead if (snapshotIsDirect) { downstreamSet.removeDirectUpstream(scheduler, depth = snapshotDirectDepth) Loading @@ -276,21 +294,14 @@ internal class DepthTracker { downstreamSet.removeIndirectUpstream( scheduler, depth = snapshotIndirectDepth, indirectSet = buildSet { addAll(snapshotIndirectRoots) if (snapshotIsIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) } }, indirectSet = snapshotIndirectRoots, ) } } } reset() } fun dirty_hasDirectUpstream(): Boolean = dirty_directUpstreamDepths.isNotEmpty() fun dirty_hasDirectUpstream(): Boolean = dirty_depthIsDirect private fun dirty_hasIndirectUpstream(): Boolean = dirty_indirectUpstreamRoots.isNotEmpty() Loading @@ -306,34 +317,39 @@ internal class DepthTracker { "dIndirectRoots=$dirty_indirectUpstreamRoots" + ")" fun reset() { snapshotIsDirect = dirty_hasDirectUpstream() fun reset(owner: MuxDeferredNode<*, *, *>?) { snapshotIsDirect = dirty_depthIsDirect snapshotDirectDepth = dirty_directDepth snapshotIndirectDepth = dirty_indirectDepth snapshotIsIndirectRoot = dirty_isIndirectRoot if (indirectAdditions.isNotEmpty() || indirectRemovals.isNotEmpty()) { if ( indirectAdditions.isNotEmpty() || indirectRemovals.isNotEmpty() || snapshotIsIndirectRoot != dirty_isIndirectRoot ) { _snapshotIndirectRoots.clear() _snapshotIndirectRoots.addAll(dirty_indirectUpstreamRoots) dirty_indirectUpstreamRoots.addAllKeysTo(_snapshotIndirectRoots) if (dirty_isIndirectRoot) { _snapshotIndirectRoots.add(owner!!) } } snapshotIsIndirectRoot = dirty_isIndirectRoot indirectAdditions.clear() indirectRemovals.clear() // check(!isDirty()) { "should not be dirty after a reset" } } fun isDirty(): Boolean = when { snapshotIsDirect -> !dirty_depthIsDirect || snapshotDirectDepth != dirty_directDepth snapshotIsIndirectRoot -> dirty_depthIsDirect || !dirty_isIndirectRoot else -> dirty_depthIsDirect || dirty_isIndirectRoot || snapshotIsIndirectRoot != dirty_isIndirectRoot || snapshotIndirectDepth != dirty_indirectDepth || indirectAdditions.isNotEmpty() || indirectRemovals.isNotEmpty() } fun dirty_depthIncreased(): Boolean = snapshotDirectDepth < dirty_directDepth || snapshotIsIndirect && dirty_hasDirectUpstream() snapshotDirectDepth < dirty_directDepth || !snapshotIsDirect && dirty_depthIsDirect } /** Loading Loading @@ -372,7 +388,7 @@ internal class DownstreamSet { fun moveIndirectUpstreamToDirect( scheduler: Scheduler, oldIndirectDepth: Int, oldIndirectSet: Set<MuxDeferredNode<*, *, *>>, oldIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, newDirectDepth: Int, ) { nodes.forEach { node -> Loading @@ -392,8 +408,8 @@ internal class DownstreamSet { scheduler: Scheduler, oldDepth: Int, newDepth: Int, removals: Set<MuxDeferredNode<*, *, *>>, additions: Set<MuxDeferredNode<*, *, *>>, removals: ScatterSet<MuxDeferredNode<*, *, *>>, additions: ScatterSet<MuxDeferredNode<*, *, *>>, ) { nodes.forEach { node -> node.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions) Loading @@ -407,7 +423,7 @@ internal class DownstreamSet { scheduler: Scheduler, oldDirectDepth: Int, newIndirectDepth: Int, newIndirectSet: Set<MuxDeferredNode<*, *, *>>, newIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { nodes.forEach { node -> node.moveDirectUpstreamToIndirect( Loading @@ -425,7 +441,7 @@ internal class DownstreamSet { fun removeIndirectUpstream( scheduler: Scheduler, depth: Int, indirectSet: Set<MuxDeferredNode<*, *, *>>, indirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { nodes.forEach { node -> node.removeIndirectUpstream(scheduler, depth, indirectSet) } muxMovers.forEach { mover -> mover.removeIndirectPatchNode(scheduler, depth, indirectSet) } Loading packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt +4 −2 Original line number Diff line number Diff line Loading @@ -38,7 +38,8 @@ internal class InputNode<A>( private val epoch get() = transactionCache.epoch override val depthTracker: DepthTracker = DepthTracker() override val depthTracker: DepthTracker get() = InputTracker override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean = epoch == evalScope.epoch Loading Loading @@ -108,7 +109,8 @@ internal fun <A> InputNode<A>.activated() = EventsImplCheap { downstream -> internal data object AlwaysNode : PushNode<Unit> { override val depthTracker = DepthTracker() override val depthTracker get() = InputTracker override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean = true Loading Loading
packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt +1 −1 Original line number Diff line number Diff line Loading @@ -836,7 +836,7 @@ internal fun BuildScope.effect( nameData: NameData, context: CoroutineContext = EmptyCoroutineContext, block: EffectScope.() -> Unit, ): Job = launchScope(nameData + "launch", context) { now.observe(name = nameData) { block() } } ): Job = launchScope(nameData + "launchScope", context) { now.observe(name = nameData) { block() } } /** * Performs a side-effect in a safe manner w/r/t the current Kairos transaction. Loading
packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt +14 −5 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import com.android.systemui.kairos.internal.util.invokeOnCancel import com.android.systemui.kairos.internal.util.launchImmediate import com.android.systemui.kairos.launchEffect import com.android.systemui.kairos.mergeLeft import com.android.systemui.kairos.skipNext import com.android.systemui.kairos.takeUntil import com.android.systemui.kairos.util.Maybe import com.android.systemui.kairos.util.Maybe.Absent Loading Loading @@ -219,7 +218,7 @@ internal class BuildScopeImpl( val newName = nameData.mapName { "$it[key=$k, epoch=$epoch, init = false]" } val newEnd: Events<Maybe<BuildSpec<A>>> = skipNext(newName + "newEnd", eventsByKey[k]) eventsByKey[k].skipNextUnsafe(newName + "newEnd") val newScope = childBuildScope(newEnd, newName) newScope.spec() } Loading Loading @@ -388,8 +387,18 @@ internal class BuildScopeImpl( val newCoroutineScope: CoroutineScope = coroutineScope.childScope() val newChildBuildScope = newChildBuildScope(newCoroutineScope, newEnd, nameData) // When the end signal emits, cancel all running coroutines in the new scope newChildBuildScope.deathSignal.observeSync(nameData + "observeLifetime") { newCoroutineScope.cancel() val outputNode = Output<Any>(nameData + "observeLifetime", onEmit = { newCoroutineScope.cancel() }) deferAction { newChildBuildScope.deathSignal.init .connect(stateScope.evalScope) .activate(stateScope.evalScope, outputNode.schedulable) ?.let { (conn, needsEval) -> outputNode.upstream = conn if (needsEval) { outputNode.schedule(0, evalScope = stateScope.evalScope) } } } return newChildBuildScope } Loading @@ -402,7 +411,7 @@ internal class BuildScopeImpl( val newCoroutineScope = coroutineScope.childScope(coroutineContext) // If the job is cancelled, emit the stop signal newCoroutineScope.coroutineContext.job.invokeOnCompletion { stopEmitter.emit(Unit) } return newChildBuildScope(newCoroutineScope, stopEmitter, nameData) return newChildBuildScope(newCoroutineScope, stopEmitter, childNameData) } private fun newChildBuildScope( Loading
packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt +6 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.kairos.internal import androidx.collection.ScatterSet import com.android.systemui.kairos.internal.store.HashMapK import com.android.systemui.kairos.internal.store.MapHolder import com.android.systemui.kairos.internal.store.MapK Loading Loading @@ -76,7 +77,7 @@ internal class DemuxNode<W, K, A>( override fun moveIndirectUpstreamToDirect( scheduler: Scheduler, oldIndirectDepth: Int, oldIndirectSet: Set<MuxDeferredNode<*, *, *>>, oldIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, newDirectDepth: Int, ) { branchNodeByKey.forEach { _, branchNode -> Loading @@ -93,8 +94,8 @@ internal class DemuxNode<W, K, A>( scheduler: Scheduler, oldDepth: Int, newDepth: Int, removals: Set<MuxDeferredNode<*, *, *>>, additions: Set<MuxDeferredNode<*, *, *>>, removals: ScatterSet<MuxDeferredNode<*, *, *>>, additions: ScatterSet<MuxDeferredNode<*, *, *>>, ) { branchNodeByKey.forEach { _, branchNode -> branchNode.downstreamSet.adjustIndirectUpstream( Loading @@ -111,7 +112,7 @@ internal class DemuxNode<W, K, A>( scheduler: Scheduler, oldDirectDepth: Int, newIndirectDepth: Int, newIndirectSet: Set<MuxDeferredNode<*, *, *>>, newIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { branchNodeByKey.forEach { _, branchNode -> branchNode.downstreamSet.moveDirectUpstreamToIndirect( Loading @@ -126,7 +127,7 @@ internal class DemuxNode<W, K, A>( override fun removeIndirectUpstream( scheduler: Scheduler, depth: Int, indirectSet: Set<MuxDeferredNode<*, *, *>>, indirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { lifecycle.lifecycleState = DemuxLifecycleState.Dead branchNodeByKey.forEach { _, branchNode -> Loading
packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt +96 −80 Original line number Diff line number Diff line Loading @@ -17,10 +17,14 @@ package com.android.systemui.kairos.internal import androidx.collection.MutableScatterSet import androidx.collection.ScatterSet import androidx.collection.mutableScatterSetOf import com.android.systemui.kairos.internal.util.Bag import com.android.systemui.kairos.internal.util.fastForEach import java.util.TreeMap internal val InputTracker = DepthTracker().apply { snapshotIsDirect = true } /** * Tracks all upstream connections for Mux nodes. * Loading Loading @@ -63,27 +67,29 @@ import java.util.TreeMap */ internal class DepthTracker { var snapshotIsDirect = true var snapshotIsDirect = false private var snapshotIsIndirectRoot = false private inline val snapshotIsIndirect: Boolean get() = !snapshotIsDirect var snapshotIndirectDepth: Int = 0 var snapshotDirectDepth: Int = 0 private val _snapshotIndirectRoots = HashSet<MuxDeferredNode<*, *, *>>() val snapshotIndirectRoots get() = _snapshotIndirectRoots.toSet() private val _snapshotIndirectRoots = MutableScatterSet<MuxDeferredNode<*, *, *>>() val snapshotIndirectRoots: ScatterSet<MuxDeferredNode<*, *, *>> get() = _snapshotIndirectRoots.toScatterSet() private val indirectAdditions = MutableScatterSet<MuxDeferredNode<*, *, *>>() private val indirectRemovals = MutableScatterSet<MuxDeferredNode<*, *, *>>() private inline val trackIndirectRootDiffs get() = !snapshotIsDirect // && !snapshotIsIndirectRoot private val indirectAdditions = HashSet<MuxDeferredNode<*, *, *>>() private val indirectRemovals = HashSet<MuxDeferredNode<*, *, *>>() private val dirty_directUpstreamDepths = TreeMap<Int, Int>() private val dirty_indirectUpstreamDepths = TreeMap<Int, Int>() private val dirty_indirectUpstreamRoots = Bag<MuxDeferredNode<*, *, *>>() var dirty_directDepth = 0 private var dirty_indirectDepth = 0 private var dirty_depthIsDirect = true private var dirty_depthIsDirect = false private var dirty_isIndirectRoot = false fun schedule(scheduler: Scheduler, node: MuxNode<*, *, *>) { Loading Loading @@ -161,32 +167,37 @@ internal class DepthTracker { } fun updateIndirectRoots( additions: Set<MuxDeferredNode<*, *, *>>? = null, removals: Set<MuxDeferredNode<*, *, *>>? = null, additions: ScatterSet<MuxDeferredNode<*, *, *>>? = null, removals: ScatterSet<MuxDeferredNode<*, *, *>>? = null, butNot: MuxDeferredNode<*, *, *>? = null, ): Boolean { val addsChanged = additions ?.let { dirty_indirectUpstreamRoots.addAll(additions, butNot) } ?.let { newlyAdded -> if (trackIndirectRootDiffs) { val remainder = indirectRemovals.applyRemovalDiff(newlyAdded) indirectAdditions.addAll(remainder) } true } ?: false val removalsChanged = removals ?.let { dirty_indirectUpstreamRoots.removeAll(removals) } ?.let { fullyRemoved -> if (trackIndirectRootDiffs) { val remainder = indirectAdditions.applyRemovalDiff(fullyRemoved) indirectRemovals.addAll(remainder) } true } ?: false return (!dirty_depthIsDirect && (addsChanged || removalsChanged)) } private fun <T> HashSet<T>.applyRemovalDiff(changeSet: HashSet<T>): Set<T> { val remainder = HashSet<T>() for (element in changeSet) { private fun <T> MutableScatterSet<T>.applyRemovalDiff(changeSet: ScatterSet<T>): ScatterSet<T> { if (isEmpty()) return changeSet val remainder = mutableScatterSetOf<T>() changeSet.forEach { element -> if (!remove(element)) { remainder.add(element) } Loading @@ -200,30 +211,32 @@ internal class DepthTracker { } } fun applyChanges( scheduler: Scheduler, downstreamSet: DownstreamSet, muxNode: MuxNode<*, *, *>, ) { private fun <T> buildScatterSet(block: MutableScatterSet<T>.() -> Unit): ScatterSet<T> = mutableScatterSetOf<T>().apply(block) private fun <T> ScatterSet<T>.toScatterSet(): ScatterSet<T> = buildScatterSet { addAll(this@toScatterSet) } fun applyChanges(scheduler: Scheduler, downstreamSet: DownstreamSet, owner: MuxNode<*, *, *>) { when { dirty_depthIsDirect -> { if (snapshotIsDirect) { val oldDepth = snapshotDirectDepth reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.adjustDirectUpstream( scheduler, oldDepth = snapshotDirectDepth, oldDepth = oldDepth, newDepth = dirty_directDepth, ) } else { val oldIndirectDepth = snapshotIndirectDepth val oldIndirectSet = snapshotIndirectRoots reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.moveIndirectUpstreamToDirect( scheduler, oldIndirectDepth = snapshotIndirectDepth, oldIndirectSet = buildSet { addAll(snapshotIndirectRoots) if (snapshotIsIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) } }, oldIndirectDepth = oldIndirectDepth, oldIndirectSet = oldIndirectSet, newDirectDepth = dirty_directDepth, ) } Loading @@ -231,44 +244,49 @@ internal class DepthTracker { dirty_hasIndirectUpstream() || dirty_isIndirectRoot -> { if (snapshotIsDirect) { val oldDirectDepth = snapshotDirectDepth val newIndirectSet = buildScatterSet { dirty_indirectUpstreamRoots.addAllKeysTo(this) if (dirty_isIndirectRoot) { add(owner as MuxDeferredNode<*, *, *>) } } reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.moveDirectUpstreamToIndirect( scheduler, oldDirectDepth = snapshotDirectDepth, oldDirectDepth = oldDirectDepth, newIndirectDepth = dirty_indirectDepth, newIndirectSet = buildSet { addAll(dirty_indirectUpstreamRoots) if (dirty_isIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) } }, newIndirectSet = newIndirectSet, ) } else { downstreamSet.adjustIndirectUpstream( scheduler, oldDepth = snapshotIndirectDepth, newDepth = dirty_indirectDepth, removals = buildSet { val oldDepth = snapshotIndirectDepth val wasIndirectRoot = snapshotIsIndirectRoot val removals = buildScatterSet { addAll(indirectRemovals) if (snapshotIsIndirectRoot && !dirty_isIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) if (wasIndirectRoot && !dirty_isIndirectRoot) { add(owner as MuxDeferredNode<*, *, *>) } }, additions = buildSet { } val additions = buildScatterSet { addAll(indirectAdditions) if (!snapshotIsIndirectRoot && dirty_isIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) if (!wasIndirectRoot && dirty_isIndirectRoot) { add(owner as MuxDeferredNode<*, *, *>) } } }, reset(owner as? MuxDeferredNode<*, *, *>) downstreamSet.adjustIndirectUpstream( scheduler, oldDepth = oldDepth, newDepth = dirty_indirectDepth, removals = removals, additions = additions, ) } } else -> { // die muxNode.lifecycle.lifecycleState = MuxLifecycleState.Dead owner.lifecycle.lifecycleState = MuxLifecycleState.Dead if (snapshotIsDirect) { downstreamSet.removeDirectUpstream(scheduler, depth = snapshotDirectDepth) Loading @@ -276,21 +294,14 @@ internal class DepthTracker { downstreamSet.removeIndirectUpstream( scheduler, depth = snapshotIndirectDepth, indirectSet = buildSet { addAll(snapshotIndirectRoots) if (snapshotIsIndirectRoot) { add(muxNode as MuxDeferredNode<*, *, *>) } }, indirectSet = snapshotIndirectRoots, ) } } } reset() } fun dirty_hasDirectUpstream(): Boolean = dirty_directUpstreamDepths.isNotEmpty() fun dirty_hasDirectUpstream(): Boolean = dirty_depthIsDirect private fun dirty_hasIndirectUpstream(): Boolean = dirty_indirectUpstreamRoots.isNotEmpty() Loading @@ -306,34 +317,39 @@ internal class DepthTracker { "dIndirectRoots=$dirty_indirectUpstreamRoots" + ")" fun reset() { snapshotIsDirect = dirty_hasDirectUpstream() fun reset(owner: MuxDeferredNode<*, *, *>?) { snapshotIsDirect = dirty_depthIsDirect snapshotDirectDepth = dirty_directDepth snapshotIndirectDepth = dirty_indirectDepth snapshotIsIndirectRoot = dirty_isIndirectRoot if (indirectAdditions.isNotEmpty() || indirectRemovals.isNotEmpty()) { if ( indirectAdditions.isNotEmpty() || indirectRemovals.isNotEmpty() || snapshotIsIndirectRoot != dirty_isIndirectRoot ) { _snapshotIndirectRoots.clear() _snapshotIndirectRoots.addAll(dirty_indirectUpstreamRoots) dirty_indirectUpstreamRoots.addAllKeysTo(_snapshotIndirectRoots) if (dirty_isIndirectRoot) { _snapshotIndirectRoots.add(owner!!) } } snapshotIsIndirectRoot = dirty_isIndirectRoot indirectAdditions.clear() indirectRemovals.clear() // check(!isDirty()) { "should not be dirty after a reset" } } fun isDirty(): Boolean = when { snapshotIsDirect -> !dirty_depthIsDirect || snapshotDirectDepth != dirty_directDepth snapshotIsIndirectRoot -> dirty_depthIsDirect || !dirty_isIndirectRoot else -> dirty_depthIsDirect || dirty_isIndirectRoot || snapshotIsIndirectRoot != dirty_isIndirectRoot || snapshotIndirectDepth != dirty_indirectDepth || indirectAdditions.isNotEmpty() || indirectRemovals.isNotEmpty() } fun dirty_depthIncreased(): Boolean = snapshotDirectDepth < dirty_directDepth || snapshotIsIndirect && dirty_hasDirectUpstream() snapshotDirectDepth < dirty_directDepth || !snapshotIsDirect && dirty_depthIsDirect } /** Loading Loading @@ -372,7 +388,7 @@ internal class DownstreamSet { fun moveIndirectUpstreamToDirect( scheduler: Scheduler, oldIndirectDepth: Int, oldIndirectSet: Set<MuxDeferredNode<*, *, *>>, oldIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, newDirectDepth: Int, ) { nodes.forEach { node -> Loading @@ -392,8 +408,8 @@ internal class DownstreamSet { scheduler: Scheduler, oldDepth: Int, newDepth: Int, removals: Set<MuxDeferredNode<*, *, *>>, additions: Set<MuxDeferredNode<*, *, *>>, removals: ScatterSet<MuxDeferredNode<*, *, *>>, additions: ScatterSet<MuxDeferredNode<*, *, *>>, ) { nodes.forEach { node -> node.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions) Loading @@ -407,7 +423,7 @@ internal class DownstreamSet { scheduler: Scheduler, oldDirectDepth: Int, newIndirectDepth: Int, newIndirectSet: Set<MuxDeferredNode<*, *, *>>, newIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { nodes.forEach { node -> node.moveDirectUpstreamToIndirect( Loading @@ -425,7 +441,7 @@ internal class DownstreamSet { fun removeIndirectUpstream( scheduler: Scheduler, depth: Int, indirectSet: Set<MuxDeferredNode<*, *, *>>, indirectSet: ScatterSet<MuxDeferredNode<*, *, *>>, ) { nodes.forEach { node -> node.removeIndirectUpstream(scheduler, depth, indirectSet) } muxMovers.forEach { mover -> mover.removeIndirectPatchNode(scheduler, depth, indirectSet) } Loading
packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt +4 −2 Original line number Diff line number Diff line Loading @@ -38,7 +38,8 @@ internal class InputNode<A>( private val epoch get() = transactionCache.epoch override val depthTracker: DepthTracker = DepthTracker() override val depthTracker: DepthTracker get() = InputTracker override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean = epoch == evalScope.epoch Loading Loading @@ -108,7 +109,8 @@ internal fun <A> InputNode<A>.activated() = EventsImplCheap { downstream -> internal data object AlwaysNode : PushNode<Unit> { override val depthTracker = DepthTracker() override val depthTracker get() = InputTracker override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean = true Loading