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

Commit 7a10baee authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] propagate transaction errors gracefully

Flag: EXEMPT unused
Test: atest kairos-tests
Change-Id: I0d48b5abf0de27a0008b3a69e49692dcca8a2fba
parent c271f98d
Loading
Loading
Loading
Loading
+2 −14
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import com.android.systemui.kairos.internal.util.childScope
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -134,19 +133,8 @@ internal class LocalFrpNetwork(
    private val scope: CoroutineScope,
    private val endSignal: TFlow<Any>,
) : FrpNetwork {
    override suspend fun <R> transact(block: suspend FrpTransactionScope.() -> R): R {
        val result = CompletableDeferred<R>(coroutineContext[Job])
        @Suppress("DeferredResultUnused")
        network.transaction("FrpNetwork.transact") {
            val buildScope =
                BuildScopeImpl(
                    stateScope = StateScopeImpl(evalScope = this, endSignal = endSignal),
                    coroutineScope = scope,
                )
            buildScope.runInBuildScope { effect { result.complete(block()) } }
        }
        return result.await()
    }
    override suspend fun <R> transact(block: suspend FrpTransactionScope.() -> R): R =
        network.transaction("FrpNetwork.transact") { runInTransactionScope { block() } }.await()

    override suspend fun activateSpec(spec: FrpSpec<*>) {
        val job =
+25 −10
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
                actions.add(func)
            }
            transactionMutex.withLock {
                try {
                    // Run all actions
                    evalScope {
                        for (action in actions) {
@@ -109,6 +110,14 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
                    }
                    // Step through the network
                    doTransaction()
                } catch (e: Exception) {
                    // Signal failure
                    while (actions.isNotEmpty()) {
                        actions.removeLast().fail(e)
                    }
                    // re-throw, cancelling this coroutine
                    throw e
                } finally {
                    // Signal completion
                    while (actions.isNotEmpty()) {
                        actions.removeLast().completed()
@@ -116,6 +125,7 @@ internal class Network(val coroutineScope: CoroutineScope) : NetworkScope {
                }
            }
        }
    }

    /** Evaluates [block] inside of a new transaction when the network is ready. */
    fun <R> transaction(reason: String, block: suspend EvalScope.() -> R): Deferred<R> =
@@ -234,6 +244,11 @@ internal class ScheduledAction<T>(
        result = just(onStartTransaction(evalScope))
    }

    fun fail(ex: Exception) {
        result = none
        onResult?.completeExceptionally(ex)
    }

    fun completed() {
        if (onResult != null) {
            when (val result = result) {
+15 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Test

class KairosTests {
@@ -1056,6 +1057,20 @@ class KairosTests {
        assertEquals(1, stateFlow.value)
    }

    @Test
    fun propagateError() {
        try {
            runFrpTest { network ->
                runCurrent()
                try {
                    network.transact<Unit> { error("message") }
                    fail("caller did not throw exception")
                } catch (_: IllegalStateException) {}
            }
            fail("scheduler did not throw exception")
        } catch (_: IllegalStateException) {}
    }

    @Test
    fun fanOutLateSubscribe() = runFrpTest { network ->
        val e = network.mutableTFlow<Map<String, Int>>()