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

Commit ce250df3 authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] simplify scope lifetime tracking

This effectively reverts ag/32952237

The issue here is that it's far too easy to accidentally leak upstream
events, and the benefit isn't that great.

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: Ic58e841c2d4f1d1cae27578ca7dbd085925a452b
parent c06ca2ca
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@ internal class LocalNetwork(
    val nameData: NameData,
    private val network: Network,
    private val scope: CoroutineScope,
    private val aliveLazy: Lazy<State<Boolean>>,
    private val deathSignalLazy: Lazy<Events<Any>>,
) : KairosNetwork {

    init {
@@ -208,7 +208,13 @@ internal class LocalNetwork(
        BuildScopeImpl(
            nameData,
            epoch,
            stateScope = StateScopeImpl(nameData, epoch, evalScope = this, aliveLazy = aliveLazy),
            stateScope =
                StateScopeImpl(
                    nameData,
                    epoch,
                    evalScope = this,
                    deathSignalLazy = deathSignalLazy,
                ),
            coroutineScope = coroutineScope,
        )

@@ -276,7 +282,7 @@ internal constructor(private val network: Network, private val scope: CoroutineS
        FullNameTag(lazyOf("root"), "launchKairosNetwork"),
        network,
        scope,
        lazyOf(stateOf(true)),
        lazyOf(emptyEvents),
    )

/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope] and [CoalescingPolicy]. */
+5 −8
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import com.android.systemui.kairos.internal.util.childScope
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.observeSync
import com.android.systemui.kairos.skipNext
import com.android.systemui.kairos.takeUntil
import com.android.systemui.kairos.util.Maybe
@@ -81,7 +80,7 @@ internal class BuildScopeImpl(
        get() = coroutineScope.coroutineContext.job

    override val kairosNetwork: LocalNetwork by lazy {
        LocalNetwork(nameData, network, coroutineScope, stateScope.aliveLazy)
        LocalNetwork(nameData, network, coroutineScope, stateScope.deathSignalLazy)
    }

    override fun <T> events(
@@ -312,7 +311,7 @@ internal class BuildScopeImpl(
                            asyncNameData,
                            network = this@BuildScopeImpl.network,
                            scope = this@newScope,
                            aliveLazy = childStateScope.aliveLazy,
                            deathSignalLazy = childStateScope.deathSignalLazy,
                        )
                    val scope =
                        object : KairosCoroutineScope, CoroutineScope by this@newScope {
@@ -327,7 +326,7 @@ internal class BuildScopeImpl(
                    nameData,
                    network = this@BuildScopeImpl.network,
                    scope = childScope,
                    aliveLazy = this@BuildScopeImpl.stateScope.aliveLazy,
                    deathSignalLazy = this@BuildScopeImpl.stateScope.deathSignalLazy,
                )
        }

@@ -401,9 +400,7 @@ internal class BuildScopeImpl(
                        (newCoroutineScope.coroutineContext.job as CompletableJob).complete()
                    }
                )
                observeSync(nameData + "observeLifetime", alive) {
                    if (!it) newCoroutineScope.cancel()
                }
                deathSignal.observeSync(nameData + "observeLifetime") { newCoroutineScope.cancel() }
            }
    }

@@ -437,7 +434,7 @@ private fun EvalScope.reenterBuildScope(
                outerScope.stateScope.nameData,
                outerScope.stateScope.createdEpoch,
                evalScope = this,
                aliveLazy = outerScope.stateScope.aliveLazy,
                deathSignalLazy = outerScope.stateScope.deathSignalLazy,
            ),
        coroutineScope,
    )
+1 −2
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.systemui.kairos.internal

import com.android.systemui.kairos.BuildScope
import com.android.systemui.kairos.Events
import com.android.systemui.kairos.State
import com.android.systemui.kairos.StateScope
import com.android.systemui.kairos.TransactionScope
import com.android.systemui.kairos.util.NameData
@@ -31,7 +30,7 @@ internal interface InitScope {
internal interface EvalScope : NetworkScope, DeferScope, TransactionScope

internal interface InternalStateScope : EvalScope, StateScope {
    val alive: State<Boolean>
    val deathSignal: Events<Any>

    fun <A> truncateToScope(events: Events<A>, nameData: NameData): Events<A>
}
+17 −37
Original line number Diff line number Diff line
@@ -27,16 +27,13 @@ import com.android.systemui.kairos.State
import com.android.systemui.kairos.StateInit
import com.android.systemui.kairos.StateScope
import com.android.systemui.kairos.Stateful
import com.android.systemui.kairos.changes
import com.android.systemui.kairos.emptyEvents
import com.android.systemui.kairos.groupByKey
import com.android.systemui.kairos.init
import com.android.systemui.kairos.map
import com.android.systemui.kairos.mapCheap
import com.android.systemui.kairos.mapCheapUnsafe
import com.android.systemui.kairos.mergeLeft
import com.android.systemui.kairos.skipNext
import com.android.systemui.kairos.switchEvents
import com.android.systemui.kairos.switchEventsPromptly
import com.android.systemui.kairos.util.Maybe
import com.android.systemui.kairos.util.NameData
import com.android.systemui.kairos.util.NameTag
@@ -50,14 +47,14 @@ internal class StateScopeImpl(
    val nameData: NameData,
    val createdEpoch: Long,
    val evalScope: EvalScope,
    val aliveLazy: Lazy<State<Boolean>>,
    val deathSignalLazy: Lazy<Events<Any>>,
) : InternalStateScope, EvalScope by evalScope {

    init {
        nameData.forceInit()
    }

    override val alive: State<Boolean> by aliveLazy
    override val deathSignal: Events<Any> by deathSignalLazy

    override fun <A> deferredStateScope(block: StateScope.() -> A): DeferredValue<A> =
        DeferredValue(deferAsync { block() })
@@ -69,7 +66,7 @@ internal class StateScopeImpl(
        val nameData = name.toNameData("Events.holdStateDeferred")
        // Ensure state is only collected until the end of this scope
        return truncateToScope(this@holdStateDeferred, nameData + "truncatedChanges")
            .holdStateInternalDeferred(nameData, this@StateScopeImpl, initialValue.unwrapped)
            .holdStateDeferredUnsafe(nameData, this@StateScopeImpl, initialValue.unwrapped)
    }

    override fun <K, V> Events<Map<K, Maybe<V>>>.foldStateMapIncrementally(
@@ -153,34 +150,17 @@ internal class StateScopeImpl(
            nameData,
            epoch,
            evalScope,
            aliveLazy =
            deathSignalLazy =
                lazy {
                    alive
                        .changes(nameData + "parentAliveChanges")
                        .mapCheap { now }
                        .holdStateInternalDeferred(
                            nameData + "deathSignalInner",
                            this,
                            deferAsync {
                                if (alive.sample()) {
                                    childEndSignal.nextOnlyInternal(
                                        nameData + "firstEndSignal",
                                        this,
                                    )
                                } else {
                                    now
                                }
                            },
                        )
                        .switchEventsPromptly(nameData + "deathSignal")
                        .mapCheap(nameData + "mapFalse") { false }
                        .holdStateInternal(nameData + "isAlive", this, true)
                    mergeLeft(nameData + "mergedDeathSignal", deathSignal, childEndSignal)
                        .nextOnlyUnsafe(nameData + "firstEndSignal", this)
                },
        )

    override fun <A> truncateToScope(events: Events<A>, nameData: NameData): Events<A> =
        alive
            .mapCheapUnsafe(nameData + "mapCheapSwitchOff") { if (it) events else emptyEvents }
        deathSignal
            .mapCheap(nameData + "switchOff") { emptyEvents }
            .holdStateUnsafe(nameData + "switchedIn", this, events)
            .switchEvents(nameData)

    override fun toString(): String = "${super.toString()}[$nameData]"
@@ -191,33 +171,33 @@ private fun EvalScope.reenterStateScope(outerScope: StateScopeImpl) =
        outerScope.nameData,
        outerScope.createdEpoch,
        evalScope = this,
        aliveLazy = outerScope.aliveLazy,
        deathSignalLazy = outerScope.deathSignalLazy,
    )

private fun <A> Events<A>.nextOnlyInternal(nameData: NameData, evalScope: EvalScope): Events<A> =
private fun <A> Events<A>.nextOnlyUnsafe(nameData: NameData, evalScope: EvalScope): Events<A> =
    if (this === emptyEvents) {
        this
    } else {
        EventsLoop<A>().apply {
            loopback =
                mapCheap(nameData + "shutoffEvent") { emptyEvents }
                    .holdStateInternal(nameData + "state", evalScope, this@nextOnlyInternal)
                    .holdStateUnsafe(nameData + "state", evalScope, this@nextOnlyUnsafe)
                    .switchEvents(nameData)
        }
    }

private fun <A> Events<A>.holdStateInternal(
private fun <A> Events<A>.holdStateUnsafe(
    nameData: NameData,
    evalScope: EvalScope,
    initialValue: A,
): State<A> = holdStateInternalDeferred(nameData, evalScope, lazyOf(initialValue))
): State<A> = holdStateDeferredUnsafe(nameData, evalScope, lazyOf(initialValue))

private fun <A> Events<A>.holdStateInternalDeferred(
private fun <A> Events<A>.holdStateDeferredUnsafe(
    nameData: NameData,
    evalScope: EvalScope,
    initialValue: Lazy<A>,
): State<A> {
    val changes = this@holdStateInternalDeferred
    val changes = this@holdStateDeferredUnsafe
    val impl =
        activatedStateSource(
            nameData,