Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81fce469 authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] Depth tracker optimizations

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: I9481c7cdfd1aaad296a8ce679eebe1a498aa463c
parent 5ffb7b53
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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.
+14 −5
Original line number Diff line number Diff line
@@ -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
@@ -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()
                        }
@@ -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
    }
@@ -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(
+6 −5
Original line number Diff line number Diff line
@@ -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
@@ -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 ->
@@ -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(
@@ -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(
@@ -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 ->
+96 −80
Original line number Diff line number Diff line
@@ -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.
 *
@@ -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<*, *, *>) {
@@ -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)
            }
@@ -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,
                    )
                }
@@ -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)
@@ -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()

@@ -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
}

/**
@@ -372,7 +388,7 @@ internal class DownstreamSet {
    fun moveIndirectUpstreamToDirect(
        scheduler: Scheduler,
        oldIndirectDepth: Int,
        oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
        oldIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>,
        newDirectDepth: Int,
    ) {
        nodes.forEach { node ->
@@ -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)
@@ -407,7 +423,7 @@ internal class DownstreamSet {
        scheduler: Scheduler,
        oldDirectDepth: Int,
        newIndirectDepth: Int,
        newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
        newIndirectSet: ScatterSet<MuxDeferredNode<*, *, *>>,
    ) {
        nodes.forEach { node ->
            node.moveDirectUpstreamToIndirect(
@@ -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) }
+4 −2
Original line number Diff line number Diff line
@@ -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
@@ -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