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

Commit 0cec81d7 authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] optimize StateScope.takeUntil

Previously, takeUntil (and nextOnly) would die once the stop signal
emitted. If the signal being truncated ends *before* the stop signal
emits, then takeUntil would remain alive; Kairos has no way of knowing
that the stop signal wouldn't switch-in a different, live Events.

This change optimizes takeUntil so that it dies once the stop signal
emits, *or* if the truncated events dies, whichever occurs first. This
is accomplished by leveraging the internal lifetime management of state
scopes, which is implemented specifically for efficiently tracking stop
signals.

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: I3d379efb377c2bdb6e6ae333aca4534a4a6cce4f
parent 81fce469
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -166,7 +166,7 @@ internal class LocalNetwork(
    val nameData: NameData,
    private val network: Network,
    private val scope: CoroutineScope,
    private val deathSignalLazy: Lazy<Events<Any>>,
    private val deathSignalLazy: Lazy<Events<*>>,
) : KairosNetwork {

    init {
+10 −28
Original line number Diff line number Diff line
@@ -631,12 +631,11 @@ interface StateScope : TransactionScope {
     * stopped. Stopping will end all state accumulation; any [States][State] returned from this
     * scope will no longer update.
     */
    fun <A> childStateScope(stop: Events<*>, stateful: Stateful<A>): DeferredValue<A> =
        childStateScope(
            nameTag("StateScope.childStateScope").toNameData("StateScope.childStateScope"),
            stop,
            stateful,
        )
    fun <A> childStateScope(
        stop: Events<*>,
        name: NameTag? = null,
        stateful: Stateful<A>,
    ): DeferredValue<A>

    /**
     * Returns an [Events] that emits values from the original [Events] up to and including a value
@@ -1369,11 +1368,7 @@ internal fun <A> StateScope.nextOnly(nameData: NameData, events: Events<A>): Eve
    if (events === emptyEvents) {
        events
    } else {
        EventsLoop<A>().apply {
            val shutOff = mapCheap(nameData + "shutOff") { emptyEvents }
            val switchedIn = holdState(nameData + "switchedIn", shutOff, events)
            loopback = switchedIn.switchEvents(nameData)
        }
        takeUntil(nameData, events, events)
    }

internal fun <A> StateScope.skipNext(nameData: NameData, events: Events<A>): Events<A> =
@@ -1392,23 +1387,10 @@ internal fun <A> StateScope.takeUntil(
    if (stop === emptyEvents) {
        events
    } else {
        val turnOff =
            nextOnly(nameData + "onlyOne", stop).mapCheap(nameData + "turnOff") { emptyEvents }
        holdState(nameData + "state", turnOff, events).switchEvents(nameData)
    }

internal fun <A> StateScope.childStateScope(
    nameData: NameData,
    stop: Events<*>,
    stateful: Stateful<A>,
): DeferredValue<A> {
    val turnOff =
        nextOnly(nameData + "onlyOne", stop).mapCheap(nameData + "turnOff") {
            mapOf(Unit to maybeOf<Stateful<A>>())
        childStateScope(stop, nameData) {
                holdState(nameData + "forTruncate", emptyEvents, events).switchEvents(nameData)
            }
    val (_, init: DeferredValue<Map<Unit, A>>) =
        applyLatestStatefulForKey(nameData, turnOff, init = mapOf(Unit to stateful), numKeys = 1)
    return deferredStateScope { init.value.getValue(Unit) }
            .defer()
    }

internal fun <A> StateScope.takeUntil(
+2 −2
Original line number Diff line number Diff line
@@ -370,7 +370,7 @@ internal class BuildScopeImpl(
            )
        emitterAndScope = constructEvents(inputNode)
        // Deactivate once scope dies, or once [builder] completes.
        val deactivateSignal: Events<Any> =
        val deactivateSignal: Events<*> =
            mergeLeft(nameData + "deactivateSignal", deathSignal, stopEmitter)
        return takeUntil(nameData + "takeUntilStopped", emitterAndScope.first, deactivateSignal)
    }
@@ -388,7 +388,7 @@ internal class BuildScopeImpl(
        val newChildBuildScope = newChildBuildScope(newCoroutineScope, newEnd, nameData)
        // When the end signal emits, cancel all running coroutines in the new scope
        val outputNode =
            Output<Any>(nameData + "observeLifetime", onEmit = { newCoroutineScope.cancel() })
            Output<Any?>(nameData + "observeLifetime", onEmit = { newCoroutineScope.cancel() })
        deferAction {
            newChildBuildScope.deathSignal.init
                .connect(stateScope.evalScope)
+1 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ internal interface InitScope {
internal interface EvalScope : NetworkScope, DeferScope, TransactionScope

internal interface InternalStateScope : EvalScope, StateScope {
    val deathSignal: Events<Any>
    val deathSignal: Events<*>

    fun <A> truncateToScope(events: Events<A>, nameData: NameData): Events<A>
}
+11 −3
Original line number Diff line number Diff line
@@ -45,14 +45,14 @@ internal class StateScopeImpl(
    val nameData: NameData,
    val createdEpoch: Long,
    val evalScope: EvalScope,
    val deathSignalLazy: Lazy<Events<Any>>,
    val deathSignalLazy: Lazy<Events<*>>,
) : InternalStateScope, EvalScope by evalScope {

    init {
        nameData.forceInit()
    }

    override val deathSignal: Events<Any> by deathSignalLazy
    override val deathSignal: Events<*> by deathSignalLazy

    override fun <A> deferredStateScope(block: StateScope.() -> A): DeferredValue<A> =
        DeferredValue(deferAsync { block() })
@@ -143,7 +143,15 @@ internal class StateScopeImpl(
        )
    }

    fun childStateScope(childEndSignal: Events<Any>, nameData: NameData) =
    override fun <A> childStateScope(
        stop: Events<*>,
        name: NameTag?,
        stateful: Stateful<A>,
    ): DeferredValue<A> =
        childStateScope(stop, name.toNameData("StateScope.childStateScope"))
            .deferredStateScope(stateful)

    fun childStateScope(childEndSignal: Events<*>, nameData: NameData) =
        StateScopeImpl(
            nameData,
            epoch,