Skip to content

Commit

Permalink
Mark the coroutine started with UNDISPATCHED as running
Browse files Browse the repository at this point in the history
Fixes #4058
  • Loading branch information
dkhalanskyjb committed Mar 26, 2024
1 parent d7c1edd commit b4361bd
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 2 deletions.
Expand Up @@ -3,3 +3,5 @@ package kotlinx.coroutines.internal
import kotlin.coroutines.*

internal expect inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T>

internal expect inline fun <T> probeCoroutineResumed(completion: Continuation<T>): Unit
4 changes: 4 additions & 0 deletions kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
Expand Up @@ -9,9 +9,12 @@ import kotlin.coroutines.intrinsics.*
* Use this function to restart a coroutine directly from inside of [suspendCoroutine],
* when the code is already in the context of this coroutine.
* It does not use [ContinuationInterceptor] and does not update the context of the current thread.
*
* Used only for tests.
*/
internal fun <T> (suspend () -> T).startCoroutineUnintercepted(completion: Continuation<T>) {
startDirect(completion) { actualCompletion ->
probeCoroutineResumed(actualCompletion)
startCoroutineUninterceptedOrReturn(actualCompletion)
}
}
Expand All @@ -24,6 +27,7 @@ internal fun <T> (suspend () -> T).startCoroutineUnintercepted(completion: Conti
internal fun <R, T> (suspend (R) -> T).startCoroutineUndispatched(receiver: R, completion: Continuation<T>) {
startDirect(completion) { actualCompletion ->
withCoroutineContext(completion.context, null) {
probeCoroutineResumed(actualCompletion)
startCoroutineUninterceptedOrReturn(receiver, actualCompletion)
}
}
Expand Down
Expand Up @@ -4,3 +4,6 @@ import kotlin.coroutines.*

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> = completion

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun <T> probeCoroutineResumed(completion: Continuation<T>) { }
8 changes: 6 additions & 2 deletions kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt
Expand Up @@ -3,6 +3,10 @@
package kotlinx.coroutines.internal

import kotlin.coroutines.*
import kotlin.coroutines.jvm.internal.probeCoroutineCreated as probe

internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> = probe(completion)
internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> =
kotlin.coroutines.jvm.internal.probeCoroutineCreated(completion)

internal actual inline fun <T> probeCoroutineResumed(completion: Continuation<T>) {
kotlinx.coroutines.debug.internal.probeCoroutineResumed(completion)
}
3 changes: 3 additions & 0 deletions kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt
Expand Up @@ -4,3 +4,6 @@ import kotlin.coroutines.*

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> = completion

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun <T> probeCoroutineResumed(completion: Continuation<T>) { }
20 changes: 20 additions & 0 deletions kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt
Expand Up @@ -106,6 +106,26 @@ class CoroutinesDumpTest : DebugTestBase() {
}
}

/**
* Tests that a coroutine started with [CoroutineStart.UNDISPATCHED] is considered running.
*/
@Test
fun testUndispatchedCoroutineIsRunning() = runBlocking {
val job = launch(Dispatchers.IO, start = CoroutineStart.UNDISPATCHED) { // or launch(Dispatchers.Unconfined)
verifyDump(
"Coroutine \"coroutine#1\":StandaloneCoroutine{Active}@1e4a7dd4, state: RUNNING\n",
ignoredCoroutine = "BlockingCoroutine"
)
delay(Long.MAX_VALUE)
}
verifyDump(
"Coroutine \"coroutine#1\":StandaloneCoroutine{Active}@1e4a7dd4, state: SUSPENDED\n",
ignoredCoroutine = "BlockingCoroutine"
) {
job.cancel()
}
}

@Test
fun testCreationStackTrace() = runBlocking {
val deferred = async(Dispatchers.IO) {
Expand Down

0 comments on commit b4361bd

Please sign in to comment.