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