From 91c87a931328ab5f2457e5bb8f09774694b1364d Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Sat, 3 Sep 2022 22:08:28 -0400 Subject: [PATCH] Add race() utility function Bug: 241121499 Test: atest RaceSuspendTest Change-Id: I4f76a335f7c87f4273d631ab095bfe4f9fe5a1dd --- .../android/systemui/util/kotlin/Suspend.kt | 35 ++++++++++++ .../systemui/util/kotlin/SuspendUtilTests.kt | 55 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt new file mode 100644 index 0000000000000..2e551f1e1beec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch + +/** + * Runs the given [blocks] in parallel, returning the result of the first one to complete, and + * cancelling all others. + */ +suspend fun race(vararg blocks: suspend () -> R): R = coroutineScope { + val completion = CompletableDeferred() + val raceJob = launch { + for (block in blocks) { + launch { completion.complete(block()) } + } + } + completion.await().also { raceJob.cancel() } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt new file mode 100644 index 0000000000000..6848b836a3489 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class RaceSuspendTest : SysuiTestCase() { + @Test + fun raceSimple() = runBlocking { + val winner = CompletableDeferred() + val result = async { + race( + { winner.await() }, + { awaitCancellation() }, + ) + } + winner.complete(1) + assertThat(result.await()).isEqualTo(1) + } + + @Test + fun raceImmediate() = runBlocking { + assertThat( + race( + { 1 }, + { 2 }, + ) + ).isEqualTo(1) + } +}