Skip to content

Commit e33c68a

Browse files
committed
Get rid of AbstractCoroutine.defaultResumeMode
It was only needed to customize resume mode from within of AbstractCoroutine.resumeWith method. This is now achieved by a separate open fun afterResume. Also, afterCompletionInternal is now renamed to afterCompletion and is only used from the job was completed from unknown context and thus should be resumed in a default way. This "default way" is now "cancellable resume" (to minimize the amount of code duplication) which does not introduce any functional difference, since that only happens on cancellation.
1 parent 3af370f commit e33c68a

File tree

7 files changed

+54
-43
lines changed

7 files changed

+54
-43
lines changed

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public abstract class kotlinx/coroutines/AbstractCoroutine : kotlinx/coroutines/
22
protected final field parentContext Lkotlin/coroutines/CoroutineContext;
33
public fun <init> (Lkotlin/coroutines/CoroutineContext;Z)V
44
public synthetic fun <init> (Lkotlin/coroutines/CoroutineContext;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
5+
protected fun afterResume (Ljava/lang/Object;)V
56
public final fun getContext ()Lkotlin/coroutines/CoroutineContext;
67
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
78
public fun isActive ()Z
@@ -380,7 +381,7 @@ public final class kotlinx/coroutines/JobKt {
380381

381382
public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlinx/coroutines/Job, kotlinx/coroutines/ParentJob, kotlinx/coroutines/selects/SelectClause0 {
382383
public fun <init> (Z)V
383-
protected fun afterCompletionInternal (Ljava/lang/Object;I)V
384+
protected fun afterCompletion (Ljava/lang/Object;)V
384385
public final fun attachChild (Lkotlinx/coroutines/ChildJob;)Lkotlinx/coroutines/ChildHandle;
385386
public synthetic fun cancel ()V
386387
public synthetic fun cancel (Ljava/lang/Throwable;)Z

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,17 @@ public abstract class AbstractCoroutine<in T>(
102102
onCompleted(state as T)
103103
}
104104

105-
internal open val defaultResumeMode: Int get() = MODE_ATOMIC_DEFAULT
106-
107105
/**
108106
* Completes execution of this with coroutine with the specified result.
109107
*/
110108
public final override fun resumeWith(result: Result<T>) {
111109
val state = makeCompletingOnce(result.toState())
112110
if (state === COMPLETING_WAITING_CHILDREN) return
113-
afterCompletionInternal(state, defaultResumeMode)
111+
afterResume(state)
114112
}
115113

114+
protected open fun afterResume(state: Any?) = afterCompletion(state)
115+
116116
internal final override fun handleOnCompletionException(exception: Throwable) {
117117
handleCoroutineException(context, exception)
118118
}

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

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
@file:JvmMultifileClass
@@ -200,7 +200,13 @@ private class UndispatchedCoroutine<in T>(
200200
context: CoroutineContext,
201201
uCont: Continuation<T>
202202
) : ScopeCoroutine<T>(context, uCont) {
203-
override val defaultResumeMode: Int get() = MODE_UNDISPATCHED
203+
override fun afterResume(state: Any?) {
204+
// resume undispatched -- update context by stay on the same dispatcher
205+
val result = recoverResult(state, uCont)
206+
withCoroutineContext(uCont.context, null) {
207+
uCont.resumeWith(result)
208+
}
209+
}
204210
}
205211

206212
private const val UNDECIDED = 0
@@ -212,8 +218,6 @@ private class DispatchedCoroutine<in T>(
212218
context: CoroutineContext,
213219
uCont: Continuation<T>
214220
) : ScopeCoroutine<T>(context, uCont) {
215-
override val defaultResumeMode: Int get() = MODE_CANCELLABLE
216-
217221
// this is copy-and-paste of a decision state machine inside AbstractionContinuation
218222
// todo: we may some-how abstract it via inline class
219223
private val _decision = atomic(UNDECIDED)
@@ -238,10 +242,16 @@ private class DispatchedCoroutine<in T>(
238242
}
239243
}
240244

241-
override fun afterCompletionInternal(state: Any?, mode: Int) {
245+
override fun afterCompletion(state: Any?) {
246+
// Call afterResume from afterCompletion and not vice-versa, because stack-size is more
247+
// important for afterResume implementation
248+
afterResume(state)
249+
}
250+
251+
override fun afterResume(state: Any?) {
242252
if (tryResume()) return // completed before getResult invocation -- bail out
243-
// otherwise, getResult has already commenced, i.e. completed later or in other thread
244-
super.afterCompletionInternal(state, mode)
253+
// Resume in a cancellable way because we have to switch back to the original dispatcher
254+
uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
245255
}
246256

247257
fun getResult(): Any? {

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines
66

77
import kotlinx.atomicfu.*
8+
import kotlinx.coroutines.internal.*
89
import kotlin.coroutines.*
910
import kotlin.jvm.*
1011

1112
internal fun <T> Result<T>.toState(): Any? =
1213
if (isSuccess) getOrThrow() else CompletedExceptionally(exceptionOrNull()!!) // todo: need to do it better
1314

15+
@Suppress("RESULT_CLASS_IN_RETURN_TYPE", "UNCHECKED_CAST")
16+
internal fun <T> recoverResult(state: Any?, uCont: Continuation<T>): Result<T> =
17+
if (state is CompletedExceptionally)
18+
Result.failure(recoverStackTrace(state.cause, uCont))
19+
else
20+
Result.success(state as T)
21+
1422
/**
1523
* Class for an internal state of a job that was cancelled (completed exceptionally).
1624
*

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

+13-14
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
269269
}
270270

271271
// fast-path method to finalize normally completed coroutines without children
272-
// returns true if complete, and afterCompletionInternal(update, mode) shall be called
272+
// returns true if complete, and afterCompletion(update) shall be called
273273
private fun tryFinalizeSimpleState(state: Incomplete, update: Any?): Boolean {
274274
assert { state is Empty || state is JobNode<*> } // only simple state without lists where children can concurrently add
275275
assert { update !is CompletedExceptionally } // only for normal completion
@@ -653,7 +653,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
653653
finalState === COMPLETING_WAITING_CHILDREN -> true
654654
finalState === TOO_LATE_TO_CANCEL -> false
655655
else -> {
656-
afterCompletionInternal(finalState, MODE_ATOMIC_DEFAULT)
656+
afterCompletion(finalState)
657657
true
658658
}
659659
}
@@ -663,7 +663,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
663663
// It contains a loop and never returns COMPLETING_RETRY, can return
664664
// COMPLETING_ALREADY -- if already completed/completing
665665
// COMPLETING_WAITING_CHILDREN -- if started waiting for children
666-
// final state -- when completed, for call to afterCompletionInternal
666+
// final state -- when completed, for call to afterCompletion
667667
private fun cancelMakeCompleting(cause: Any?): Any? {
668668
loopOnState { state ->
669669
if (state !is Incomplete || state is Finishing && state.isCompleting) {
@@ -703,7 +703,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
703703
// COMPLETING_ALREADY -- if already completing or successfully made cancelling, added exception
704704
// COMPLETING_WAITING_CHILDREN -- if started waiting for children, added exception
705705
// TOO_LATE_TO_CANCEL -- too late to cancel, did not add exception
706-
// final state -- when completed, for call to afterCompletionInternal
706+
// final state -- when completed, for call to afterCompletion
707707
private fun makeCancelling(cause: Any?): Any? {
708708
var causeExceptionCache: Throwable? = null // lazily init result of createCauseException(cause)
709709
loopOnState { state ->
@@ -786,7 +786,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
786786
finalState === COMPLETING_WAITING_CHILDREN -> return true
787787
finalState === COMPLETING_RETRY -> return@loopOnState
788788
else -> {
789-
afterCompletionInternal(finalState, MODE_ATOMIC_DEFAULT)
789+
afterCompletion(finalState)
790790
return true
791791
}
792792
}
@@ -798,7 +798,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
798798
* It throws [IllegalStateException] on repeated invocation (when this job is already completing).
799799
* Returns:
800800
* * [COMPLETING_WAITING_CHILDREN] if started waiting for children.
801-
* * Final state otherwise (caller should do [afterCompletionInternal])
801+
* * Final state otherwise (caller should do [afterCompletion])
802802
*/
803803
internal fun makeCompletingOnce(proposedUpdate: Any?): Any? {
804804
loopOnState { state ->
@@ -819,7 +819,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
819819
// COMPLETING_ALREADY -- when already complete or completing
820820
// COMPLETING_RETRY -- when need to retry due to interference
821821
// COMPLETING_WAITING_CHILDREN -- when made completing and is waiting for children
822-
// final state -- when completed, for call to afterCompletionInternal
822+
// final state -- when completed, for call to afterCompletion
823823
private fun tryMakeCompleting(state: Any?, proposedUpdate: Any?): Any? {
824824
if (state !is Incomplete)
825825
return COMPLETING_ALREADY
@@ -844,7 +844,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
844844
// COMPLETING_ALREADY -- when already complete or completing
845845
// COMPLETING_RETRY -- when need to retry due to interference
846846
// COMPLETING_WAITING_CHILDREN -- when made completing and is waiting for children
847-
// final state -- when completed, for call to afterCompletionInternal
847+
// final state -- when completed, for call to afterCompletion
848848
private fun tryMakeCompletingSlowPath(state: Incomplete, proposedUpdate: Any?): Any? {
849849
// get state's list or else promote to list to correctly operate on child lists
850850
val list = getOrPromoteCancellingList(state) ?: return COMPLETING_RETRY
@@ -910,7 +910,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
910910
if (waitChild != null && tryWaitForChild(state, waitChild, proposedUpdate)) return // waiting for next child
911911
// no more children to wait -- try update state
912912
val finalState = tryFinalizeFinishingState(state, proposedUpdate)
913-
afterCompletionInternal(finalState, MODE_ATOMIC_DEFAULT)
913+
afterCompletion(finalState)
914914
}
915915

916916
private fun LockFreeLinkedListNode.nextChild(): ChildHandleNode? {
@@ -1014,14 +1014,13 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
10141014
protected open fun onCompletionInternal(state: Any?) {}
10151015

10161016
/**
1017-
* Override for the very last action on job's completion to resume the rest of the code in scoped coroutines.
1018-
*
1019-
* @param state the final state.
1020-
* @param mode completion mode.
1017+
* Override for the very last action on job's completion to resume the rest of the code in
1018+
* scoped coroutines. It is called when this job is externally completed in an unknown
1019+
* context and thus should resume with a default mode.
10211020
*
10221021
* @suppress **This is unstable API and it is subject to change.**
10231022
*/
1024-
protected open fun afterCompletionInternal(state: Any?, mode: Int) {}
1023+
protected open fun afterCompletion(state: Any?) {}
10251024

10261025
// for nicer debugging
10271026
public override fun toString(): String =

kotlinx-coroutines-core/common/src/internal/Scopes.kt

+8-15
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,16 @@ internal open class ScopeCoroutine<in T>(
2020
final override fun getStackTraceElement(): StackTraceElement? = null
2121
final override val isScopedCoroutine: Boolean get() = true
2222

23-
override val defaultResumeMode: Int get() = MODE_DIRECT
24-
2523
internal val parent: Job? get() = parentContext[Job]
2624

27-
@Suppress("UNCHECKED_CAST")
28-
override fun afterCompletionInternal(state: Any?, mode: Int) {
29-
val result = if (state is CompletedExceptionally)
30-
Result.failure(recoverStackTrace(state.cause, uCont))
31-
else
32-
Result.success(state as T)
33-
when (mode) {
34-
MODE_ATOMIC_DEFAULT -> uCont.intercepted().resumeWith(result)
35-
MODE_CANCELLABLE -> uCont.intercepted().resumeCancellableWith(result)
36-
MODE_DIRECT -> uCont.resumeWith(result)
37-
MODE_UNDISPATCHED -> withCoroutineContext(uCont.context, null) { uCont.resumeWith(result) }
38-
else -> error("Invalid mode $mode")
39-
}
25+
override fun afterCompletion(state: Any?) {
26+
// Resume in a cancellable way by default when resuming from another context
27+
uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
28+
}
29+
30+
override fun afterResume(state: Any?) {
31+
// Resume direct because scope is already in the correct context
32+
uCont.resumeWith(recoverResult(state, uCont))
4033
}
4134
}
4235

kotlinx-coroutines-core/jvm/src/Builders.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
@file:JvmMultifileClass
@@ -61,7 +61,7 @@ private class BlockingCoroutine<T>(
6161
) : AbstractCoroutine<T>(parentContext, true) {
6262
override val isScopedCoroutine: Boolean get() = true
6363

64-
override fun afterCompletionInternal(state: Any?, mode: Int) {
64+
override fun afterCompletion(state: Any?) {
6565
// wake up blocked thread
6666
if (Thread.currentThread() != blockedThread)
6767
LockSupport.unpark(blockedThread)

0 commit comments

Comments
 (0)