Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions kotlinx-coroutines-core/common/src/CancellableContinuation.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kotlinx.coroutines

import kotlinx.coroutines.internal.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*

Expand Down Expand Up @@ -424,10 +425,12 @@ internal fun <T> CancellableContinuation<T>.invokeOnCancellation(handler: Cancel
* [CoroutineDispatcher] class, then there is no prompt cancellation guarantee. A custom continuation interceptor
* can resume execution of a previously suspended coroutine even if its job was already cancelled.
*/
@OptIn(ExperimentalContracts::class)
public suspend inline fun <T> suspendCancellableCoroutine(
crossinline block: (CancellableContinuation<T>) -> Unit
): T =
suspendCoroutineUninterceptedOrReturn { uCont ->
): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return suspendCoroutineUninterceptedOrReturn { uCont ->
val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
/*
* For non-atomic cancellation we setup parent-child relationship immediately
Expand All @@ -438,6 +441,7 @@ public suspend inline fun <T> suspendCancellableCoroutine(
block(cancellable)
cancellable.getResult()
}
}

/**
* Suspends the coroutine similar to [suspendCancellableCoroutine], but an instance of
Expand Down
11 changes: 11 additions & 0 deletions kotlinx-coroutines-core/common/test/CancellableContinuationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,15 @@ class CancellableContinuationTest : TestBase() {
})
finish(5)
}

/** Tests that the compiler recognizes that [suspendCancellableCoroutine] invokes its block exactly once. */
@Test
fun testSuspendCancellableCoroutineContract() = runTest {
val i: Int
suspendCancellableCoroutine { cont ->
i = 1
cont.resume(Unit)
}
assertEquals(1, i)
}
}