Skip to content

Commit 4757879

Browse files
committed
Fully copy CoroutineInfo for DebugProbes.dumpCoroutinesInfo, it is required for IDEA integration (field is left as internal deliberately)
1 parent 46b5ea5 commit 4757879

File tree

3 files changed

+18
-11
lines changed

3 files changed

+18
-11
lines changed

kotlinx-coroutines-debug/src/CoroutineInfo.kt

+4-8
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,10 @@ public data class CoroutineInfo internal constructor(
4444
@JvmField
4545
internal var lastObservedFrame: CoroutineStackFrame? = null
4646

47-
// Copy constructor
48-
internal constructor(coroutine: Continuation<*>, state: CoroutineInfo) : this(
49-
coroutine.context,
50-
state.creationStackBottom,
51-
state.sequenceNumber
52-
) {
53-
_state = state.state
54-
this.lastObservedFrame = state.lastObservedFrame
47+
public fun copy(): CoroutineInfo = CoroutineInfo(context, creationStackBottom, sequenceNumber).also {
48+
it._state = _state
49+
it.lastObservedFrame = lastObservedFrame
50+
it.lastObservedThread = lastObservedThread
5551
}
5652

5753
/**

kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ internal object DebugProbesImpl {
8080
check(isInstalled) { "Debug probes are not installed" }
8181
val jobToStack = capturedCoroutines
8282
.filter { it.delegate.context[Job] != null }
83-
.associateBy({ it.delegate.context[Job]!! }, {it.info})
83+
.associateBy({ it.delegate.context[Job]!! }, { it.info })
8484
return buildString {
8585
job.build(jobToStack, this, "")
8686
}
@@ -118,7 +118,7 @@ internal object DebugProbesImpl {
118118
public fun dumpCoroutinesInfo(): List<CoroutineInfo> {
119119
check(isInstalled) { "Debug probes are not installed" }
120120
return capturedCoroutines.asSequence()
121-
.map { CoroutineInfo(it.delegate, it.info) }
121+
.map { it.info.copy() } // Copy as CoroutineInfo can be mutated concurrently by DebugProbes
122122
.sortedBy { it.sequenceNumber }
123123
.toList()
124124
}
@@ -373,7 +373,7 @@ internal object DebugProbesImpl {
373373
private fun <T : Throwable> sanitizeStackTrace(throwable: T): List<StackTraceElement> {
374374
val stackTrace = throwable.stackTrace
375375
val size = stackTrace.size
376-
val probeIndex = stackTrace.indexOfLast { it.className == "kotlin.coroutines.jvm.internal.DebugProbesKt" }
376+
val probeIndex = stackTrace.indexOfLast { it.className == "kotlin.coroutines.jvm.internal.DebugProbesKt" }
377377

378378
if (!DebugProbes.sanitizeStackTraces) {
379379
return List(size - probeIndex) {

kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt

+11
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,15 @@ class RunningThreadStackMergeTest : DebugTestBase() {
167167
yield()
168168
assertTrue(true)
169169
}
170+
171+
@Test
172+
fun testActiveThread() = runBlocking<Unit> {
173+
launchCoroutine()
174+
awaitCoroutineStarted()
175+
val info = DebugProbes.dumpCoroutinesInfo().find { it.state == State.RUNNING }
176+
assertNotNull(info)
177+
@Suppress("INVISIBLE_MEMBER") // IDEA bug
178+
assertNotNull(info.lastObservedThread)
179+
coroutineBlocker.await()
180+
}
170181
}

0 commit comments

Comments
 (0)