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

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

[kairos] replace statescope endsignal w/ explicit alive state

Model the lifetime of the state scope explicitly as a state, which can
then be used to switch off events via map and switchEvents().

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: I514e00d998ed52367907c858b61b464fcb45c566
parent 1cd4c618
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.kairos

import com.android.systemui.kairos.internal.CompletableLazy

/**
 * A value that may not be immediately (synchronously) available, but is guaranteed to be available
 * before this transaction is completed.
@@ -33,5 +31,4 @@ class DeferredValue<out A> internal constructor(internal val unwrapped: Lazy<A>)
}

/** Returns an already-available [DeferredValue] containing [value]. */
@ExperimentalKairosApi
fun <A> deferredOf(value: A): DeferredValue<A> = DeferredValue(CompletableLazy(value))
@ExperimentalKairosApi fun <A> deferredOf(value: A): DeferredValue<A> = DeferredValue(lazyOf(value))
+1 −1
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ internal constructor(
     */
    fun emit(value: In) {
        val (scheduled, _) =
            storage.getAndUpdate { (_, batch) -> true to CompletableLazy(coalesce(batch, value)) }
            storage.getAndUpdate { (_, batch) -> true to lazyOf(coalesce(batch, value)) }
        if (!scheduled) {
            @Suppress("DeferredResultUnused")
            network.transaction(
+13 −17
Original line number Diff line number Diff line
@@ -17,11 +17,12 @@
package com.android.systemui.kairos

import com.android.systemui.kairos.internal.BuildScopeImpl
import com.android.systemui.kairos.internal.EvalScope
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.awaitCancellationAndThen
import com.android.systemui.kairos.internal.util.childScope
import com.android.systemui.kairos.internal.util.invokeOnCancel
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CancellationException
@@ -142,32 +143,21 @@ suspend fun <R> KairosNetwork.activateSpec(
internal class LocalNetwork(
    private val network: Network,
    private val scope: CoroutineScope,
    private val endSignalLazy: Lazy<Events<Any>>,
    private val aliveLazy: Lazy<State<Boolean>>,
) : KairosNetwork {

    override suspend fun <R> transact(block: TransactionScope.() -> R): R =
        network.transaction("KairosNetwork.transact") { block() }.awaitOrCancel()

    override suspend fun activateSpec(spec: BuildSpec<*>): Unit = coroutineScope {
        val stopEmitter = conflatedMutableEvents<Unit>()
        lateinit var completionHandle: DisposableHandle
        val childEndSignal = conflatedMutableEvents<Unit>().apply { invokeOnCancel { emit(Unit) } }
        val job =
            launch(start = CoroutineStart.LAZY) {
                network
                    .transaction("KairosNetwork.activateSpec") {
                        val buildScope =
                            BuildScopeImpl(
                                stateScope =
                                    StateScopeImpl(
                                        evalScope = this,
                                        endSignalLazy =
                                            lazy { mergeLeft(stopEmitter, endSignalLazy.value) },
                                    ),
                                coroutineScope = this@coroutineScope,
                            )
                        buildScope.launchScope {
                            spec.applySpec()
                            launchEffect { awaitCancellationAndThen { stopEmitter.emit(Unit) } }
                        reenterBuildScope(this@coroutineScope).childBuildScope(childEndSignal).run {
                            launchScope { spec.applySpec() }
                        }
                    }
                    .awaitOrCancel()
@@ -178,6 +168,12 @@ internal class LocalNetwork(
        job.start()
    }

    private fun EvalScope.reenterBuildScope(coroutineScope: CoroutineScope) =
        BuildScopeImpl(
            stateScope = StateScopeImpl(evalScope = this, aliveLazy = aliveLazy),
            coroutineScope = coroutineScope,
        )

    private suspend fun <T> Deferred<T>.awaitOrCancel(): T =
        try {
            await()
@@ -226,7 +222,7 @@ internal class LocalNetwork(
@ExperimentalKairosApi
class RootKairosNetwork
internal constructor(private val network: Network, private val scope: CoroutineScope, job: Job) :
    Job by job, KairosNetwork by LocalNetwork(network, scope, lazyOf(emptyEvents))
    Job by job, KairosNetwork by LocalNetwork(network, scope, lazyOf(stateOf(true)))

/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope] and [CoalescingPolicy]. */
@ExperimentalKairosApi
+1 −1
Original line number Diff line number Diff line
@@ -257,7 +257,7 @@ class MutableState<T> internal constructor(internal val network: Network, initia
     * Multiple invocations of [setValue] that occur before a transaction are conflated; only the
     * most recent value is used.
     */
    fun setValue(value: T) = input.emit(CompletableLazy(value))
    fun setValue(value: T) = input.emit(lazyOf(value))

    /**
     * Sets the value held by this [State]. The [DeferredValue] will not be queried until this
+2 −2
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ interface StateScope : TransactionScope {

    /**
     * Returns an [Events] containing the results of applying each [Stateful] emitted from the
     * original [Events], and a [DeferredValue] containing the result of applying [init]
     * original [Events], and a [DeferredValue] containing the result of applying [initialValues]
     * immediately.
     *
     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
@@ -105,7 +105,7 @@ interface StateScope : TransactionScope {
     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
     */
    fun <K, A, B> Events<MapPatch<K, Stateful<A>>>.applyLatestStatefulForKey(
        init: DeferredValue<Map<K, Stateful<B>>>,
        initialValues: DeferredValue<Map<K, Stateful<B>>>,
        numKeys: Int? = null,
    ): Pair<Events<MapPatch<K, A>>, DeferredValue<Map<K, B>>>

Loading