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

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

[kairos] configurable input event coalescing policy

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: I73adc01e66a5ac6e7f6ac1cb1d0920f82eb622b3
parent 64a95068
Loading
Loading
Loading
Loading
+37 −3
Original line number Diff line number Diff line
@@ -229,17 +229,51 @@ class RootKairosNetwork
internal constructor(private val network: Network, private val scope: CoroutineScope, job: Job) :
    Job by job, KairosNetwork by LocalNetwork(network, scope, lazyOf(emptyEvents))

/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope]. */
/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope] and [CoalescingPolicy]. */
@ExperimentalKairosApi
fun CoroutineScope.launchKairosNetwork(
    context: CoroutineContext = EmptyCoroutineContext
    context: CoroutineContext = EmptyCoroutineContext,
    coalescingPolicy: CoalescingPolicy = CoalescingPolicy.Normal,
): RootKairosNetwork {
    val scope = childScope(context)
    val network = Network(scope)
    val network = Network(scope, coalescingPolicy)
    scope.launch(CoroutineName("launchKairosNetwork scheduler")) { network.runInputScheduler() }
    return RootKairosNetwork(network, scope, scope.coroutineContext.job)
}

/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope] and [CoalescingPolicy]. */
fun KairosNetwork(
    scope: CoroutineScope,
    coalescingPolicy: CoalescingPolicy = CoalescingPolicy.Normal,
): RootKairosNetwork = scope.launchKairosNetwork(coalescingPolicy = coalescingPolicy)

/** Configures how multiple input events are processed by the network. */
enum class CoalescingPolicy {
    /**
     * Each input event is processed in its own transaction. This policy has the least overhead but
     * can cause backpressure if the network becomes flooded with inputs.
     */
    None,
    /**
     * Input events are processed as they appear. Compared to [Eager], this policy will not
     * internally [yield][kotlinx.coroutines.yield] to allow more inputs to be processed before
     * starting a transaction. This means that if there is a race between an input and a transaction
     * occurring, it is beholden to the
     * [CoroutineDispatcher][kotlinx.coroutines.CoroutineDispatcher] to determine the ordering.
     *
     * Note that any input events which miss being included in a transaction will be immediately
     * scheduled for a subsequent transaction.
     */
    Normal,
    /**
     * Input events are processed eagerly. Compared to [Normal], this policy will internally
     * [yield][kotlinx.coroutines.yield] to allow for as many input events to be processed as
     * possible. This can be useful for noisy networks where many inputs can be handled
     * simultaneously, potentially improving throughput.
     */
    Eager,
}

@ExperimentalKairosApi
interface HasNetwork : KairosScope {
    /**
+18 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.kairos.internal

import com.android.systemui.kairos.CoalescingPolicy
import com.android.systemui.kairos.State
import com.android.systemui.kairos.internal.util.HeteroMap
import com.android.systemui.kairos.internal.util.logDuration
@@ -40,7 +41,8 @@ import kotlinx.coroutines.yield

private val nextNetworkId = AtomicLong()

internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
internal class Network(val coroutineScope: CoroutineScope, val coalescingPolicy: CoalescingPolicy) :
    NetworkScope {

    override val networkId: Any = nextNetworkId.getAndIncrement()

@@ -103,11 +105,22 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
        for (first in inputScheduleChan) {
            // Drain and conflate all transaction requests into a single transaction
            actions.add(first)
            when (coalescingPolicy) {
                CoalescingPolicy.None -> {}
                CoalescingPolicy.Normal -> {
                    while (true) {
                        val func = inputScheduleChan.tryReceive().getOrNull() ?: break
                        actions.add(func)
                    }
                }
                CoalescingPolicy.Eager -> {
                    while (true) {
                        yield()
                        val func = inputScheduleChan.tryReceive().getOrNull() ?: break
                        actions.add(func)
                    }
                }
            }
            transactionMutex.withLock {
                val e = epoch
                val duration = measureTime {