Merge "Quick cancellation of effect scopes" into androidx-main am: dd48cf473c

Original change: https://android-review.googlesource.com/c/platform/frameworks/support/+/2583774

Change-Id: I6bdd1a9d7d44818d3c19888318356137bb21e8f5
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
index 1254875..9efcc11 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
@@ -22,6 +22,7 @@
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
 import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CancellationException
 
 /**
  * Schedule [effect] to run when the current composition completes successfully and applies
@@ -284,17 +285,18 @@
     private var job: Job? = null
 
     override fun onRemembered() {
+        // This should never happen but is left here for safety
         job?.cancel("Old job was still running!")
         job = scope.launch(block = task)
     }
 
     override fun onForgotten() {
-        job?.cancel()
+        job?.cancel(LeftCompositionCancellationException())
         job = null
     }
 
     override fun onAbandoned() {
-        job?.cancel()
+        job?.cancel(LeftCompositionCancellationException())
         job = null
     }
 }
@@ -384,6 +386,12 @@
     remember(key1, key2, key3) { LaunchedEffectImpl(applyContext, block) }
 }
 
+private class LeftCompositionCancellationException : CancellationException(
+    "The coroutine scope left the composition"
+) {
+    override fun fillInStackTrace(): Throwable = this
+}
+
 /**
  * When [LaunchedEffect] enters the composition it will launch [block] into the composition's
  * [CoroutineContext]. The coroutine will be [cancelled][Job.cancel] and **re-launched** when
@@ -416,11 +424,11 @@
     }
 
     override fun onForgotten() {
-        coroutineScope.cancel()
+        coroutineScope.cancel(LeftCompositionCancellationException())
     }
 
     override fun onAbandoned() {
-        coroutineScope.cancel()
+        coroutineScope.cancel(LeftCompositionCancellationException())
     }
 }