From d487c1ef0f1380740c5848c7c026c5635978206c Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 11 Jan 2017 20:22:26 +0300 Subject: [PATCH] Refine dispatching convention Dispatching happens only via `suspendWithCurrentContinuation` calls instead of each `resume` call #KT-15657 Fixed --- .../coroutines/coroutineCodegenUtil.kt | 12 +++++ .../codegen/box/coroutines/dispatchResume.kt | 4 +- .../dispatchResume.kt | 4 +- .../src/kotlin/jvm/internal/CoroutineImpl.kt | 34 ++++--------- .../jvm/internal/CoroutineIntrinsics.kt | 50 +++++++++++++++++++ .../reference-public-api/kotlin-runtime.txt | 6 +++ 6 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/CoroutineIntrinsics.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 3825107ed23..22d27b04460 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -60,6 +60,9 @@ const val COROUTINE_LABEL_FIELD_NAME = "label" const val SUSPEND_FUNCTION_CREATE_METHOD_NAME = "create" const val DO_RESUME_METHOD_NAME = "doResume" +private val INTERNAL_COROUTINE_INTRINSICS_OWNER = "kotlin/jvm/internal/CoroutineIntrinsics" +private val NORMALIZE_CONTINUATION_METHOD_NAME = "normalizeContinuation" + data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression) @JvmField @@ -207,6 +210,15 @@ fun createMethodNodeForSuspendCoroutineOrReturn( node.visitVarInsn(Opcodes.ALOAD, 0) node.visitVarInsn(Opcodes.ALOAD, 1) + + node.visitMethodInsn( + Opcodes.INVOKESTATIC, + INTERNAL_COROUTINE_INTRINSICS_OWNER, + NORMALIZE_CONTINUATION_METHOD_NAME, + Type.getMethodDescriptor(AsmTypes.CONTINUATION, AsmTypes.CONTINUATION), + false + ) + node.visitMethodInsn( Opcodes.INVOKEINTERFACE, typeMapper.mapType(functionDescriptor.valueParameters[0]).internalName, diff --git a/compiler/testData/codegen/box/coroutines/dispatchResume.kt b/compiler/testData/codegen/box/coroutines/dispatchResume.kt index 455ab3bee0f..86d12a32660 100644 --- a/compiler/testData/codegen/box/coroutines/dispatchResume.kt +++ b/compiler/testData/codegen/box/coroutines/dispatchResume.kt @@ -53,7 +53,7 @@ fun box(): String { val k = suspendWithValue("K") log += "$o$k;" } - if (result != "before 0;suspend(O);before 1;suspend(K);before 2;OK;after 2;after 1;after 0;") return "fail1: $result" + if (result != "suspend(O);before 0;suspend(K);before 1;OK;after 1;after 0;") return "fail1: $result" result = test { try { @@ -64,7 +64,7 @@ fun box(): String { log += "${e.message};" } } - if (result != "before 0;error(OK);before 1;OK;after 1;after 0;") return "fail2: $result" + if (result != "error(OK);before 0;OK;after 0;") return "fail2: $result" return "OK" } diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt index f351ef3a809..555e1c4a716 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt @@ -71,12 +71,12 @@ fun box(): String { var result = test { test1() } - if (result != "before 0;suspend();before 1;suspend(O);before 2;before 3;suspend(K);before 4;OK;before 5;after 5;after 4;after 3;after 2;after 1;after 0;") return "fail1: $result" + if (result != "suspend();before 0;suspend(O);before 1;suspend(K);before 2;OK;after 2;after 1;after 0;") return "fail1: $result" result = test { test2() } - if (result != "before 0;suspend();before 1;suspend(O);before 2;before 3;error(OK);before 4;OK;before 5;after 5;after 4;after 3;after 2;after 1;after 0;") return "fail2: $result" + if (result != "suspend();before 0;suspend(O);before 1;error(OK);before 2;OK;after 2;after 1;after 0;") return "fail2: $result" return "OK" } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt index 93c31127234..f7bf6d2c8ee 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt @@ -19,41 +19,25 @@ package kotlin.jvm.internal import kotlin.coroutines.* import kotlin.coroutines.intrinsics.SUSPENDED_MARKER -private const val INTERCEPT_BIT_SET = 1 shl 31 -private const val INTERCEPT_BIT_CLEAR = INTERCEPT_BIT_SET.inv() - abstract class CoroutineImpl : RestrictedCoroutineImpl, DispatchedContinuation { private val _dispatcher: ContinuationDispatcher? override val dispatcher: ContinuationDispatcher? get() = _dispatcher + private var facade_: Continuation? = null + val facade: Continuation get() { + if (facade_ == null) { + facade_ = wrapContinuationIfNeeded(this, dispatcher) + } + + return facade_!! + } + // this constructor is used to create a continuation instance for coroutine constructor(arity: Int, completion: Continuation?) : super(arity, completion) { _dispatcher = (completion as? DispatchedContinuation<*>)?.dispatcher } - - override fun resume(value: Any?) { - if (_dispatcher != null) { - if (label and INTERCEPT_BIT_SET == 0) { - label = label or INTERCEPT_BIT_SET - if (_dispatcher.dispatchResume(value, this)) return - } - label = label and INTERCEPT_BIT_CLEAR - } - super.resume(value) - } - - override fun resumeWithException(exception: Throwable) { - if (_dispatcher != null) { - if (label and INTERCEPT_BIT_SET == 0) { - label = label or INTERCEPT_BIT_SET - if (_dispatcher.dispatchResumeWithException(exception, this)) return - } - label = label and INTERCEPT_BIT_CLEAR - } - super.resumeWithException(exception) - } } abstract class RestrictedCoroutineImpl : Lambda, Continuation { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineIntrinsics.kt b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineIntrinsics.kt new file mode 100644 index 00000000000..0c239d4675f --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineIntrinsics.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:kotlin.jvm.JvmName("CoroutineIntrinsics") +package kotlin.jvm.internal + +import kotlin.coroutines.Continuation +import kotlin.coroutines.ContinuationDispatcher + +fun normalizeContinuation(c: Continuation): Continuation { + if (c is CoroutineImpl) { + return c.facade + } + + return c +} + +internal fun wrapContinuationIfNeeded(c: Continuation, dispatcher: ContinuationDispatcher?): Continuation { + if (dispatcher == null) return c + return DispatchedContinuationImpl(c, dispatcher) +} + +private class DispatchedContinuationImpl( + private val c: Continuation, + private val dispatcher: ContinuationDispatcher +) : Continuation { + override fun resume(value: T) { + if (!dispatcher.dispatchResume(value, c)) { + c.resume(value) + } + } + + override fun resumeWithException(exception: Throwable) { + if (!dispatcher.dispatchResumeWithException(exception, c)) { + c.resumeWithException(exception) + } + } +} diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt index b4ef83525cd..dc201ae3a3b 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt @@ -471,10 +471,16 @@ public class kotlin/jvm/internal/CollectionToArray { public abstract class kotlin/jvm/internal/CoroutineImpl : kotlin/jvm/internal/RestrictedCoroutineImpl, kotlin/jvm/internal/DispatchedContinuation { public fun (ILkotlin/coroutines/Continuation;)V public fun getDispatcher ()Lkotlin/coroutines/ContinuationDispatcher; + public final fun getFacade ()Lkotlin/coroutines/Continuation; public fun resume (Ljava/lang/Object;)V public fun resumeWithException (Ljava/lang/Throwable;)V } +public final class kotlin/jvm/internal/CoroutineIntrinsics { + public static final fun normalizeContinuation (Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; + public static final fun wrapContinuationIfNeeded (Lkotlin/coroutines/Continuation;Lkotlin/coroutines/ContinuationDispatcher;)Lkotlin/coroutines/Continuation; +} + public abstract interface class kotlin/jvm/internal/DispatchedContinuation : kotlin/coroutines/Continuation { public abstract fun getDispatcher ()Lkotlin/coroutines/ContinuationDispatcher; }