xref: /aosp_15_r20/frameworks/base/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.kairos.internal
18 
19 import com.android.systemui.kairos.internal.util.Key
20 import com.android.systemui.kairos.util.Maybe
21 import com.android.systemui.kairos.util.just
22 import java.util.concurrent.atomic.AtomicBoolean
23 import kotlinx.coroutines.coroutineScope
24 import kotlinx.coroutines.sync.Mutex
25 import kotlinx.coroutines.sync.withLock
26 
27 internal class InputNode<A>(
28     private val activate: suspend EvalScope.() -> Unit = {},
<lambda>null29     private val deactivate: () -> Unit = {},
30 ) : PushNode<A>, Key<A> {
31 
32     internal val downstreamSet = DownstreamSet()
33     private val mutex = Mutex()
34     private val activated = AtomicBoolean(false)
35 
36     override val depthTracker: DepthTracker = DepthTracker()
37 
hasCurrentValuenull38     override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean =
39         transactionStore.contains(this)
40 
41     suspend fun visit(evalScope: EvalScope, value: A) {
42         evalScope.setResult(this, value)
43         coroutineScope {
44             if (!mutex.withLock { scheduleAll(downstreamSet, evalScope) }) {
45                 evalScope.scheduleDeactivation(this@InputNode)
46             }
47         }
48     }
49 
removeDownstreamnull50     override suspend fun removeDownstream(downstream: Schedulable) {
51         mutex.withLock { downstreamSet.remove(downstream) }
52     }
53 
deactivateIfNeedednull54     override suspend fun deactivateIfNeeded() {
55         if (mutex.withLock { downstreamSet.isEmpty() && activated.getAndSet(false) }) {
56             deactivate()
57         }
58     }
59 
scheduleDeactivationIfNeedednull60     override suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
61         if (mutex.withLock { downstreamSet.isEmpty() }) {
62             evalScope.scheduleDeactivation(this)
63         }
64     }
65 
addDownstreamnull66     override suspend fun addDownstream(downstream: Schedulable) {
67         mutex.withLock { downstreamSet.add(downstream) }
68     }
69 
addDownstreamAndActivateIfNeedednull70     suspend fun addDownstreamAndActivateIfNeeded(downstream: Schedulable, evalScope: EvalScope) {
71         val needsActivation =
72             mutex.withLock {
73                 val wasEmpty = downstreamSet.isEmpty()
74                 downstreamSet.add(downstream)
75                 wasEmpty && !activated.getAndSet(true)
76             }
77         if (needsActivation) {
78             activate(evalScope)
79         }
80     }
81 
removeDownstreamAndDeactivateIfNeedednull82     override suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
83         val needsDeactivation =
84             mutex.withLock {
85                 downstreamSet.remove(downstream)
86                 downstreamSet.isEmpty() && activated.getAndSet(false)
87             }
88         if (needsDeactivation) {
89             deactivate()
90         }
91     }
92 
getPushEventnull93     override suspend fun getPushEvent(evalScope: EvalScope): Maybe<A> =
94         evalScope.getCurrentValue(this)
95 }
96 
97 internal fun <A> InputNode<A>.activated() = TFlowCheap { downstream ->
98     val input = this@activated
99     addDownstreamAndActivateIfNeeded(downstream, evalScope = this)
100     ActivationResult(connection = NodeConnection(input, input), needsEval = hasCurrentValue(input))
101 }
102 
103 internal data object AlwaysNode : PushNode<Unit> {
104 
105     override val depthTracker = DepthTracker()
106 
hasCurrentValuenull107     override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean = true
108 
109     override suspend fun removeDownstream(downstream: Schedulable) {}
110 
deactivateIfNeedednull111     override suspend fun deactivateIfNeeded() {}
112 
scheduleDeactivationIfNeedednull113     override suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {}
114 
addDownstreamnull115     override suspend fun addDownstream(downstream: Schedulable) {}
116 
removeDownstreamAndDeactivateIfNeedednull117     override suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {}
118 
getPushEventnull119     override suspend fun getPushEvent(evalScope: EvalScope): Maybe<Unit> = just(Unit)
120 }
121