Wrap continuation with ContinuationImpl on callable references
in startCoroutineUninterceptedOrReturn. Otherwise, the coroutine will not be interceptable later. Add a test, which checks, that intercepted continuation is released. #KT-55869
This commit is contained in:
committed by
Space Team
parent
9a2cb2609f
commit
d18672bfb1
+11
@@ -55,6 +55,17 @@ fun box(): String {
|
||||
if (res != "OK") {
|
||||
return "fail 3 $res"
|
||||
}
|
||||
res = builder(::suspendHere)
|
||||
if (res != "OK") {
|
||||
return "fail 4 $res"
|
||||
}
|
||||
res = builder {
|
||||
suspend {}()
|
||||
suspendHere()
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 5 $res"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+24
@@ -56,6 +56,30 @@ fun box(): String {
|
||||
if (res != "OK") {
|
||||
return "fail 3 $res"
|
||||
}
|
||||
res = c.builder {
|
||||
suspend {}()
|
||||
controllerSuspendHere()
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 4 $res"
|
||||
}
|
||||
res = c.builder {
|
||||
suspend {}()
|
||||
controllerMultipleArgs(1, 1, 1)
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 5 $res"
|
||||
}
|
||||
res = c.builder {
|
||||
suspend {}()
|
||||
if (coroutineContext != EmptyCoroutineContext)
|
||||
"${coroutineContext} != $EmptyCoroutineContext"
|
||||
else
|
||||
"OK"
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 6 $res"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Vendored
+21
@@ -55,6 +55,27 @@ fun box(): String {
|
||||
if (res != "OK") {
|
||||
return "fail 3 $res"
|
||||
}
|
||||
res = v.builder {
|
||||
suspend {}()
|
||||
controllerMultipleArgs(1, 1, 1)
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 4 $res"
|
||||
}
|
||||
res = v.builder {
|
||||
suspend {}()
|
||||
if (coroutineContext == EmptyCoroutineContext) "$coroutineContext == $EmptyCoroutineContext" else "OK"
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 5 $res"
|
||||
}
|
||||
res = v.builder {
|
||||
suspend {}()
|
||||
controllerSuspendHere()
|
||||
}
|
||||
if (res != "OK") {
|
||||
return "fail 6 $res"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+41
-20
@@ -1,5 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
// WITH_COROUTINES
|
||||
// IGNORE_BACKEND: JS_IR, NATIVE
|
||||
import helpers.*
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
@@ -25,28 +26,24 @@ suspend fun suspendWithExceptionIntercepted(): String = suspendCoroutineUninterc
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(expectedCount: Int, c: suspend () -> String): String {
|
||||
fun builder(testNum: Int, expectedCount: Int, c: suspend () -> String): String {
|
||||
var fromSuspension: String? = null
|
||||
var counter = 0
|
||||
|
||||
val result = try {
|
||||
c.startCoroutineUninterceptedOrReturn(object: ContinuationAdapter<String>() {
|
||||
override val context: CoroutineContext
|
||||
get() = ContinuationDispatcher { counter++ }
|
||||
c.startCoroutineUninterceptedOrReturn(object : ContinuationAdapter<String>() {
|
||||
override val context: CoroutineContext
|
||||
get() = ContinuationDispatcher { counter++ }
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
fromSuspension = "Exception: " + exception.message!!
|
||||
}
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
fromSuspension = "Exception: " + exception.message!!
|
||||
}
|
||||
|
||||
override fun resume(value: String) {
|
||||
fromSuspension = value
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
"Exception: ${e.message}"
|
||||
}
|
||||
override fun resume(value: String) {
|
||||
fromSuspension = value
|
||||
}
|
||||
})
|
||||
|
||||
if (counter != expectedCount) throw RuntimeException("fail 0")
|
||||
if (counter != expectedCount) throw RuntimeException("fail 0 $testNum $counter != $expectedCount")
|
||||
return fromSuspension!!
|
||||
}
|
||||
|
||||
@@ -72,11 +69,35 @@ private class DispatchedContinuation<T>(
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (builder(0) { suspendHereUnintercepted() } != "OK") return "fail 2"
|
||||
if (builder(1) { suspendHereIntercepted() } != "OK") return "fail 3"
|
||||
if (builder(0, 0) { suspendHereUnintercepted() } != "OK") return "fail 2"
|
||||
if (builder(1, 1) { suspendHereIntercepted() } != "OK") return "fail 3"
|
||||
|
||||
if (builder(0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4"
|
||||
if (builder(1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5"
|
||||
if (builder(2, 0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4"
|
||||
if (builder(3, 1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5"
|
||||
|
||||
if (builder(4, 0, ::suspendHereUnintercepted) != "OK") return "fail 6"
|
||||
if (builder(5, 1, ::suspendHereIntercepted) != "OK") return "fail 7"
|
||||
|
||||
if (builder(6, 0, ::suspendWithExceptionUnintercepted) != "Exception: OK") return "fail 8"
|
||||
if (builder(7, 1, ::suspendWithExceptionIntercepted) != "Exception: OK") return "fail 9"
|
||||
|
||||
if (builder(8, 0) {
|
||||
suspend {}()
|
||||
suspendHereUnintercepted()
|
||||
} != "OK") return "fail 21"
|
||||
if (builder(9, 1) {
|
||||
suspend {}()
|
||||
suspendHereIntercepted()
|
||||
} != "OK") return "fail 31"
|
||||
|
||||
if (builder(10, 0) {
|
||||
suspend {}()
|
||||
suspendWithExceptionUnintercepted()
|
||||
} != "Exception: OK") return "fail 41"
|
||||
if (builder(11, 1) {
|
||||
suspend {}()
|
||||
suspendWithExceptionIntercepted()
|
||||
} != "Exception: OK") return "fail 51"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// WITH_STDLIB
|
||||
// IGNORE_BACKEND: JS_IR, NATIVE
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
|
||||
var c: Continuation<String>? = null
|
||||
var interceptions: Int = 0
|
||||
var releases: Int = 0
|
||||
var invocations: Int = 0
|
||||
|
||||
suspend fun suspendHere(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
invocations++
|
||||
c = x
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendHereIntercepted(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
invocations++
|
||||
c = x.intercepted()
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(testNum: Int, expectedResult: Result<String>, c: suspend () -> String) {
|
||||
c.startCoroutineUninterceptedOrReturn(object : Continuation<String> {
|
||||
override val context: CoroutineContext
|
||||
get() = ContinuationDispatcher()
|
||||
|
||||
override fun resumeWith(result: Result<String>) {
|
||||
if (result != expectedResult) {
|
||||
error("fail $testNum: unexpected result: $expectedResult != $result")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class ContinuationDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
|
||||
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = DispatchedContinuation(continuation)
|
||||
|
||||
override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
|
||||
releases++
|
||||
}
|
||||
}
|
||||
|
||||
private class DispatchedContinuation<T>(
|
||||
val continuation: Continuation<T>
|
||||
): Continuation<T> {
|
||||
init {
|
||||
interceptions++
|
||||
}
|
||||
|
||||
override val context: CoroutineContext = continuation.context
|
||||
|
||||
override fun resumeWith(result: Result<T>) {
|
||||
continuation.resumeWith(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val success = Result.success("OK")
|
||||
|
||||
builder(0, success) { suspendHere() }
|
||||
if (invocations != 1) return "fail 00: $invocations"
|
||||
if (interceptions != 0) return "fail 01: $interceptions"
|
||||
if (releases != 0) return "fail 02: $releases"
|
||||
c = c?.intercepted()
|
||||
if (interceptions != 1) return "fail 03: $interceptions"
|
||||
c?.resumeWith(success)
|
||||
if (releases != 1) return "fail 04: $releases"
|
||||
|
||||
builder(1, success) { suspendHereIntercepted() }
|
||||
if (invocations != 2) return "fail 10: $invocations"
|
||||
if (interceptions != 2) return "fail 11: $interceptions"
|
||||
if (releases != 1) return "fail 12: $interceptions"
|
||||
c?.resumeWith(success)
|
||||
if (interceptions != 2) return "fail 13: $interceptions"
|
||||
if (releases != 2) return "fail 14: $releases"
|
||||
|
||||
builder(2, success, ::suspendHere)
|
||||
if (invocations != 3) return "fail 20: $invocations"
|
||||
if (interceptions != 2) return "fail 21: $interceptions"
|
||||
if (releases != 2) return "fail 22: $releases"
|
||||
c = c?.intercepted()
|
||||
if (interceptions != 3) return "fail 23: $interceptions"
|
||||
c?.resumeWith(success)
|
||||
if (releases != 3) return "fail 24: $releases"
|
||||
|
||||
builder(3, success, ::suspendHereIntercepted)
|
||||
if (invocations != 4) return "fail 30: $invocations"
|
||||
if (interceptions != 4) return "fail 31: $interceptions"
|
||||
if (releases != 3) return "fail 32: $interceptions"
|
||||
c?.resumeWith(success)
|
||||
if (interceptions != 4) return "fail 33: $interceptions"
|
||||
if (releases != 4) return "fail 34: $releases"
|
||||
|
||||
builder(4, success) { suspend {}(); suspendHere() }
|
||||
if (invocations != 5) return "fail 40: $invocations"
|
||||
if (interceptions != 4) return "fail 41: $interceptions"
|
||||
if (releases != 4) return "fail 42: $releases"
|
||||
c = c?.intercepted()
|
||||
if (interceptions != 5) return "fail 43: $interceptions"
|
||||
c?.resumeWith(success)
|
||||
if (releases != 5) return "fail 44: $releases"
|
||||
|
||||
builder(5, success) { suspend {}(); suspendHereIntercepted() }
|
||||
if (invocations != 6) return "fail 50: $invocations"
|
||||
if (interceptions != 6) return "fail 51: $interceptions"
|
||||
if (releases != 5) return "fail 52: $interceptions"
|
||||
c?.resumeWith(success)
|
||||
if (interceptions != 6) return "fail 53: $interceptions"
|
||||
if (releases != 6) return "fail 54: $releases"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+11
@@ -37,9 +37,20 @@ fun builder(c: suspend () -> String): String {
|
||||
fun box(): String {
|
||||
if (builder { "OK" } != "OK") return "fail 4"
|
||||
if (builder { suspendHere() } != "OK") return "fail 5"
|
||||
if (builder {
|
||||
suspend {}()
|
||||
suspendHere()
|
||||
} != "OK") return "fail 51"
|
||||
|
||||
if (builder { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
|
||||
if (builder { suspendWithException() } != "Exception: OK") return "fail 7"
|
||||
if (builder {
|
||||
suspend {}()
|
||||
suspendWithException()
|
||||
} != "Exception: OK") return "fail 71"
|
||||
|
||||
if (builder(::suspendHere) != "OK") return "fail 8"
|
||||
if (builder(::suspendWithException) != "Exception: OK") return "fail 9"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Vendored
+5
@@ -48,9 +48,14 @@ fun builder(shouldSuspend: Boolean, c: suspend () -> String): String {
|
||||
fun box(): String {
|
||||
if (builder(false) { "OK" } != "OK") return "fail 4"
|
||||
if (builder(true) { suspendHere() } != "OK") return "fail 5"
|
||||
if (builder(true) { suspend{}(); suspendHere() } != "OK") return "fail 51"
|
||||
|
||||
if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
|
||||
if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7"
|
||||
if (builder(true) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71"
|
||||
|
||||
if (builder(true, ::suspendHere) != "OK") return "fail 8"
|
||||
if (builder(true, ::suspendWithException) != "Exception: OK") return "fail 9"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+15
-9
@@ -1,5 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
// WITH_COROUTINES
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
import helpers.*
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
@@ -19,7 +20,7 @@ suspend fun suspendWithException(): String = suspendCoroutine { x ->
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String): String {
|
||||
fun builder(testNum: Int, shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String): String {
|
||||
callback = {}
|
||||
var fromSuspension: String? = null
|
||||
var counter = 0
|
||||
@@ -43,15 +44,15 @@ fun builder(shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String)
|
||||
|
||||
callback()
|
||||
|
||||
if (counter != expectedCount) throw RuntimeException("fail 0")
|
||||
if (counter != expectedCount) throw RuntimeException("fail 0 $testNum $counter != $expectedCount")
|
||||
|
||||
if (shouldSuspend) {
|
||||
if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1")
|
||||
if (fromSuspension == null) throw RuntimeException("fail 2")
|
||||
if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1 $testNum $result")
|
||||
if (fromSuspension == null) throw RuntimeException("fail 2 $testNum")
|
||||
return fromSuspension!!
|
||||
}
|
||||
|
||||
if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3")
|
||||
if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3 $testNum")
|
||||
return result as String
|
||||
}
|
||||
|
||||
@@ -77,11 +78,16 @@ private class DispatchedContinuation<T>(
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (builder(false, 0) { "OK" } != "OK") return "fail 4"
|
||||
if (builder(true, 1) { suspendHere() } != "OK") return "fail 5"
|
||||
if (builder(0, false, 0) { "OK" } != "OK") return "fail 4"
|
||||
if (builder(1, true, 1) { suspendHere() } != "OK") return "fail 5"
|
||||
if (builder(2, true, 1) { suspend{}(); suspendHere() } != "OK") return "fail 51"
|
||||
|
||||
if (builder(false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
|
||||
if (builder(true, 1) { suspendWithException() } != "Exception: OK") return "fail 7"
|
||||
if (builder(3, false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
|
||||
if (builder(4, true, 1) { suspendWithException() } != "Exception: OK") return "fail 7"
|
||||
if (builder(5, true, 1) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71"
|
||||
|
||||
if (builder(6, true, 1, ::suspendHere) != "OK") return "fail 8"
|
||||
if (builder(7, true, 1, ::suspendWithException) != "Exception: OK") return "fail 9"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt
Vendored
+5
@@ -72,9 +72,14 @@ private class DispatchedContinuation<T>(
|
||||
fun box(): String {
|
||||
if (builder(false) { "OK" } != "OK") return "fail 4"
|
||||
if (builder(true) { suspendHere() } != "OK") return "fail 5"
|
||||
if (builder(true) { suspend{}(); suspendHere() } != "OK") return "fail 51"
|
||||
|
||||
if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
|
||||
if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7"
|
||||
if (builder(true) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71"
|
||||
|
||||
if (builder(true, ::suspendHere) != "OK") return "fail 8"
|
||||
if (builder(true, ::suspendWithException) != "Exception: OK") return "fail 9"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user