Skip to content

Commit d766f37

Browse files
LouisCADrecheej
authored andcommitted
Add awaitCancellation (Kotlin#2225)
Fixes Kotlin#2213
1 parent ceca65f commit d766f37

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

kotlinx-coroutines-core/api/kotlinx-coroutines-core.api

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ public final class kotlinx/coroutines/Delay$DefaultImpls {
267267
}
268268

269269
public final class kotlinx/coroutines/DelayKt {
270+
public static final fun awaitCancellation (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
270271
public static final fun delay (JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
271272
public static final fun delay-p9JZ4hM (DLkotlin/coroutines/Continuation;)Ljava/lang/Object;
272273
}

kotlinx-coroutines-core/common/src/Delay.kt

+41
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,51 @@ public interface Delay {
5858
DefaultDelay.invokeOnTimeout(timeMillis, block)
5959
}
6060

61+
/**
62+
* Suspends until cancellation, in which case it will throw a [CancellationException].
63+
*
64+
* This function returns [Nothing], so it can be used in any coroutine,
65+
* regardless of the required return type.
66+
*
67+
* Usage example in callback adapting code:
68+
*
69+
* ```kotlin
70+
* fun currentTemperature(): Flow<Temperature> = callbackFlow {
71+
* val callback = SensorCallback { degreesCelsius: Double ->
72+
* trySend(Temperature.celsius(degreesCelsius))
73+
* }
74+
* try {
75+
* registerSensorCallback(callback)
76+
* awaitCancellation() // Suspends to keep getting updates until cancellation.
77+
* } finally {
78+
* unregisterSensorCallback(callback)
79+
* }
80+
* }
81+
* ```
82+
*
83+
* Usage example in (non declarative) UI code:
84+
*
85+
* ```kotlin
86+
* suspend fun showStuffUntilCancelled(content: Stuff): Nothing {
87+
* someSubView.text = content.title
88+
* anotherSubView.text = content.description
89+
* someView.visibleInScope {
90+
* awaitCancellation() // Suspends so the view stays visible.
91+
* }
92+
* }
93+
* ```
94+
*/
95+
@ExperimentalCoroutinesApi
96+
public suspend fun awaitCancellation(): Nothing = suspendCancellableCoroutine {}
97+
6198
/**
6299
* Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
63100
* This suspending function is cancellable.
64101
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
65102
* immediately resumes with [CancellationException].
66103
*
104+
* If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
105+
*
67106
* Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
68107
*
69108
* Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
@@ -82,6 +121,8 @@ public suspend fun delay(timeMillis: Long) {
82121
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
83122
* immediately resumes with [CancellationException].
84123
*
124+
* If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
125+
*
85126
* Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
86127
*
87128
* Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.coroutines
6+
7+
import kotlin.test.*
8+
9+
class AwaitCancellationTest : TestBase() {
10+
11+
@Test
12+
fun testCancellation() = runTest(expected = { it is CancellationException }) {
13+
expect(1)
14+
coroutineScope {
15+
val deferred: Deferred<Nothing> = async {
16+
expect(2)
17+
awaitCancellation()
18+
}
19+
yield()
20+
expect(3)
21+
require(deferred.isActive)
22+
deferred.cancel()
23+
finish(4)
24+
deferred.await()
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)