diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.skiko.kt index 1f9af873deca1..dbee98acde810 100644 --- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.skiko.kt +++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.skiko.kt @@ -100,16 +100,22 @@ internal class FlushCoroutineDispatcher( synchronized(tasksLock) { delayedTasks.add(block) } - scope.launch { - kotlinx.coroutines.delay(timeMillis) - performRun { - val isTaskAlive = synchronized(tasksLock) { - delayedTasks.remove(block) - } - if (isTaskAlive) { - block.run() + val job = scope.launch { + try{ + kotlinx.coroutines.delay(timeMillis) + } finally { + performRun { + val isTaskAlive = synchronized(tasksLock) { + delayedTasks.remove(block) + } + if (isTaskAlive) { + block.run() + } } } } + continuation.invokeOnCancellation { + job.cancel() + } } } diff --git a/compose/ui/ui/src/skikoTest/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcherTest.kt b/compose/ui/ui/src/skikoTest/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcherTest.kt index cdebd234eff09..5f235b3758d80 100644 --- a/compose/ui/ui/src/skikoTest/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcherTest.kt +++ b/compose/ui/ui/src/skikoTest/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcherTest.kt @@ -16,16 +16,13 @@ package androidx.compose.ui.platform -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.yield import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse -import kotlinx.coroutines.withContext +import kotlin.test.assertTrue +import kotlinx.coroutines.* @OptIn(ExperimentalCoroutinesApi::class) class FlushCoroutineDispatcherTest { @@ -106,4 +103,19 @@ class FlushCoroutineDispatcherTest { assertEquals((0 until 10000).toList(), actualNumbers) assertFalse(dispatcher.hasTasks()) } + + @Test + fun delayed_tasks_are_cancelled() = runTest { + val coroutineScope = CoroutineScope(Dispatchers.Unconfined) + val dispatcher = FlushCoroutineDispatcher(coroutineScope) + val job = launch(dispatcher){ + delay(Long.MAX_VALUE/2) + } + assertTrue(dispatcher.hasTasks()) + job.cancel() + assertTrue( + !dispatcher.hasTasks(), + "FlushCoroutineDispatcher has a delayed task that has been cancelled" + ) + } }