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

Commit 094b8171 authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] bypass unnecessary internal state accum

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: I257352127ccd19eef50d8cb9057ca181df975a09
parent 8f2f30e7
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -854,11 +854,10 @@ internal fun BuildScope.effect(
 * Generally, you should prefer [effect] over this method.
 *
 * ```
 *   fun BuildScope.effectImmediate(
 *       context: CoroutineContext = EmptyCoroutineContext,
 *       block: EffectScope.() -> Unit,
 *   fun BuildScope.effectSync(
 *       block: TransactionEffectScope.() -> Unit,
 *   ): Job =
 *       launchScope(context) { now.observeImmediate { block() } }
 *       launchScope { now.observeSync { block() } }
 * ```
 *
 * @see effect
+5 −5
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import com.android.systemui.kairos.internal.Network
import com.android.systemui.kairos.internal.NoScope
import com.android.systemui.kairos.internal.StateScopeImpl
import com.android.systemui.kairos.internal.util.childScope
import com.android.systemui.kairos.internal.util.invokeOnCancel
import com.android.systemui.kairos.util.FullNameTag
import com.android.systemui.kairos.util.NameData
import com.android.systemui.kairos.util.NameTag
@@ -180,9 +179,10 @@ internal class LocalNetwork(
    override suspend fun activateSpec(name: NameTag?, spec: BuildSpec<*>): Unit = coroutineScope {
        val nameData = name.toNameData("KairosNetwork.activateSpec")
        lateinit var completionHandle: DisposableHandle
        val childEndSignal =
            conflatedMutableEvents<Unit>(nameData.mapName { "$it-specEndSignal" }).apply {
                invokeOnCancel { emit(Unit) }
        val childEndSignal = conflatedMutableEvents<Unit>(nameData.mapName { "$it-specEndSignal" })
        coroutineContext.job.invokeOnCompletion {
            completionHandle.dispose()
            childEndSignal.emit(Unit)
        }
        val job =
            launch(start = CoroutineStart.LAZY) {
+4 −4
Original line number Diff line number Diff line
@@ -1370,9 +1370,9 @@ internal fun <A> StateScope.nextOnly(nameData: NameData, events: Events<A>): Eve
        events
    } else {
        EventsLoop<A>().apply {
            val shutoff = mapCheap(nameData + "shutoff") { emptyEvents }
            val state = holdState(nameData + "state", shutoff, events)
            loopback = state.switchEvents(nameData)
            val shutOff = mapCheap(nameData + "shutOff") { emptyEvents }
            val switchedIn = holdState(nameData + "switchedIn", shutOff, events)
            loopback = switchedIn.switchEvents(nameData)
        }
    }

@@ -1381,7 +1381,7 @@ internal fun <A> StateScope.skipNext(nameData: NameData, events: Events<A>): Eve
        events
    } else {
        val turnOn = nextOnly(nameData + "onlyOne", events).mapCheap(nameData + "turnOn") { events }
        holdState(nameData + "state", turnOn, emptyEvents).switchEvents(nameData)
        holdState(nameData + "switchedIn", turnOn, emptyEvents).switchEvents(nameData)
    }

internal fun <A> StateScope.takeUntil(
+38 −34
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ 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.mergeLeft
import com.android.systemui.kairos.skipNext
import com.android.systemui.kairos.takeUntil
import com.android.systemui.kairos.util.Maybe
@@ -241,9 +242,7 @@ internal class BuildScopeImpl(
    ): DisposableHandle {
        val subRef = AtomicReference<Maybe<Output<A>>?>(null)
        val childScope: CoroutineScope = coroutineScope.childScope(context)
        var cancelHandle: DisposableHandle? = null
        val handle = DisposableHandle {
            cancelHandle?.dispose()
            subRef.getAndSet(Absent)?.let { output ->
                if (output is Present) {
                    @Suppress("DeferredResultUnused")
@@ -253,8 +252,6 @@ internal class BuildScopeImpl(
                }
            }
        }
        // When our scope is cancelled, deactivate this observer.
        cancelHandle = childScope.coroutineContext.job.invokeOnCompletion { handle.dispose() }
        val effectScope: EffectScope = effectScope(childScope, nameData + "effectScope")
        val outputNode =
            Output<A>(
@@ -269,7 +266,11 @@ internal class BuildScopeImpl(
        // Defer, in case any EventsLoops / StateLoops still need to be set
        deferAction {
            // Check for immediate cancellation
            if (subRef.get() != null) return@deferAction
            if (subRef.get() != null) {
                childScope.cancel()
                return@deferAction
            }
            // Stop observing when this scope dies
            truncateToScope(this@observeInternal, nameData + "truncateToScope")
                .init
                .connect(evalScope = stateScope.evalScope)
@@ -277,7 +278,7 @@ internal class BuildScopeImpl(
                ?.let { (conn, needsEval) ->
                    outputNode.upstream = conn
                    if (!subRef.compareAndSet(null, Maybe.present(outputNode))) {
                        // Job's already been cancelled, schedule deactivation
                        // Handle's already been disposed, schedule deactivation
                        scheduleDeactivation(outputNode)
                    } else if (needsEval) {
                        outputNode.schedule(0, evalScope = stateScope.evalScope)
@@ -369,10 +370,10 @@ internal class BuildScopeImpl(
                },
            )
        emitterAndScope = constructEvents(inputNode)
        return truncateToScope(
            takeUntil(nameData + "takeUntilStopped", emitterAndScope.first, stopEmitter),
            nameData + "scopeLifetimeBound",
        )
        // Deactivate once scope dies, or once [builder] completes.
        val deactivateSignal: Events<Any> =
            mergeLeft(nameData + "deactivateSignal", deathSignal, stopEmitter)
        return takeUntil(nameData + "takeUntilStopped", emitterAndScope.first, deactivateSignal)
    }

    private fun newStopEmitter(nameData: NameData): CoalescingMutableEvents<Unit, Unit> =
@@ -385,39 +386,42 @@ internal class BuildScopeImpl(

    fun childBuildScope(newEnd: Events<Any>, nameData: NameData): BuildScopeImpl {
        val newCoroutineScope: CoroutineScope = coroutineScope.childScope()
        return BuildScopeImpl(
                nameData,
                epoch,
                stateScope = stateScope.childStateScope(newEnd, nameData),
                coroutineScope = newCoroutineScope,
            )
            .apply {
                // Ensure that once this transaction is done, the new child scope enters the
                // completing state (kept alive so long as there are child jobs).
                scheduleOutput(
                    OneShot(nameData + "completeJob") {
                        // TODO: don't like this cast
                        (newCoroutineScope.coroutineContext.job as CompletableJob).complete()
                    }
                )
                deathSignal.observeSync(nameData + "observeLifetime") { newCoroutineScope.cancel() }
        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()
        }
        return newChildBuildScope
    }

    private fun mutableChildBuildScope(
        childNameData: NameData,
        coroutineContext: CoroutineContext,
    ): BuildScopeImpl {
        val childScope = coroutineScope.childScope(coroutineContext)
        val stopEmitter =
            newStopEmitter(childNameData + "stopEmitter").apply {
                childScope.invokeOnCancel { emit(Unit) }
        val stopEmitter = newStopEmitter(childNameData + "stopEmitter")
        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)
    }

    private fun newChildBuildScope(
        newCoroutineScope: CoroutineScope,
        newEnd: Events<Any>,
        nameData: NameData,
    ): BuildScopeImpl {
        // Ensure that once this transaction is done, the new child scope enters the completing
        // state (kept alive so long as there are child jobs).
        scheduleOutput(
            OneShot(nameData + "completeJob") {
                (newCoroutineScope.coroutineContext.job as CompletableJob).complete()
            }
        )
        return BuildScopeImpl(
            childNameData,
            nameData,
            epoch,
            stateScope = stateScope.childStateScope(stopEmitter, childNameData),
            coroutineScope = childScope,
            stateScope = stateScope.childStateScope(newEnd, nameData),
            coroutineScope = newCoroutineScope,
        )
    }
}
+8 −16
Original line number Diff line number Diff line
@@ -54,32 +54,24 @@ internal class EvalScopeImpl(networkScope: NetworkScope, deferScope: DeferScope)
    override val now: Events<Unit> by lazy {
        var result by EventsLoop<Unit>()
        val switchOff = result.mapCheap { emptyEvents }
        val nameTag = nameTag { "now(epoc=$epoch)" }.toNameData("now")
        val nameTag = nameTag { "now(epoch=$epoch)" }.toNameData("TransactionScope.now")
        result =
            StateInit(
                    constInit(
                        nameTag,
                        activatedStateSource(
                            nameTag,
                            nameTag + "switchedIn",
                            this,
                            { switchOff.init.connect(evalScope = this) },
                            lazyOf(
                                EventsInit(
                                    constInit(
                                        nameTag,
                                        EventsImplCheap {
                                            ActivationResult(
                                                connection = NodeConnection(AlwaysNode, AlwaysNode),
                                                needsEval = true,
                                            )
                                        },
                                    )
                                )
                            ),
                            lazyOf(EventsInit(constInit(nameTag + "always", alwaysImpl))),
                        ),
                    )
                )
                .switchEvents(nameTag + "onlyOnce")
                .switchEvents(nameTag)
        result
    }
}

private val alwaysImpl = EventsImplCheap {
    ActivationResult(connection = NodeConnection(AlwaysNode, AlwaysNode), needsEval = true)
}
Loading