mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-14 18:05:27 +07:00
IJPL-156281 make coroutine dumper resilient to circular job dependencies
GitOrigin-RevId: 95d64a8fca56c22a2fd9c3d0a7efd29c39ff8126
This commit is contained in:
committed by
intellij-monorepo-bot
parent
58e9edebab
commit
fe1a45223e
@@ -159,22 +159,29 @@ private fun jobTrees(scope: CoroutineScope? = null): Sequence<JobTree> {
|
||||
|
||||
return sequence {
|
||||
for (job in rootJobs) {
|
||||
yieldAll(buildJobTrees(job, jobToStack))
|
||||
yieldAll(buildJobTrees(job, jobToStack, hashSetOf()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildJobTrees(
|
||||
job: Job,
|
||||
jobToStack: Map<Job, DebugCoroutineInfo>
|
||||
jobToStack: Map<Job, DebugCoroutineInfo>,
|
||||
visited: MutableSet<Job>
|
||||
): List<JobTree> {
|
||||
return visited.withElement(job) { notSeenThisJob ->
|
||||
if (notSeenThisJob) {
|
||||
val info = jobToStack[job]
|
||||
if (info === null && job is ScopeCoroutine<*>) {
|
||||
// don't yield ScopeCoroutine without info, such as `coroutineScope` or `withContext`
|
||||
return job.children.flatMap { buildJobTrees(it, jobToStack) }.toList()
|
||||
job.children.flatMap { buildJobTrees(it, jobToStack, visited) }.toList()
|
||||
}
|
||||
else {
|
||||
return listOf(JobTree(job, info, job.children.flatMap { buildJobTrees(it, jobToStack) }.toList()))
|
||||
listOf(JobTree(job, info, job.children.flatMap { buildJobTrees(it, jobToStack, visited) }.toList()))
|
||||
}
|
||||
} else {
|
||||
listOf(JobTree(RecursiveJob(job), null, emptyList()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,3 +316,19 @@ private fun traceToDump(info: DebugCoroutineInfo, stripTrace: Boolean): List<Sta
|
||||
}
|
||||
return DebugProbesImpl.enhanceStackTraceWithThreadDump(info, trace)
|
||||
}
|
||||
|
||||
private fun <T, R> MutableSet<T>.withElement(elem: T, body: (added: Boolean) -> R): R {
|
||||
val added = add(elem)
|
||||
try {
|
||||
return body(added)
|
||||
}
|
||||
finally {
|
||||
if (added) remove(elem)
|
||||
}
|
||||
}
|
||||
|
||||
private class RecursiveJob(private val originalJob: Job) : Job by originalJob {
|
||||
override fun toString(): String {
|
||||
return "CIRCULAR REFERENCE: $originalJob"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.diagnostic
|
||||
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import kotlinx.coroutines.*
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Timeout
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class CoroutineDumpTest {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
fun enableDumps() {
|
||||
enableCoroutineDump()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(InternalCoroutinesApi::class)
|
||||
@Suppress("SSBasedInspection", "DEPRECATION_ERROR")
|
||||
@Test
|
||||
@Timeout(value = 5, unit = TimeUnit.SECONDS)
|
||||
fun testRecursiveJobsDump() {
|
||||
val projectScope = CoroutineScope(CoroutineName("Project"))
|
||||
val pluginScope = CoroutineScope(CoroutineName("Plugin"))
|
||||
val activity = projectScope.childScope("Project Activity")
|
||||
pluginScope.coroutineContext[Job]!!.attachChild(activity.coroutineContext[Job]!! as ChildJob)
|
||||
// e.g. bug here
|
||||
activity.coroutineContext[Job]!!.attachChild(pluginScope.coroutineContext[Job]!! as ChildJob)
|
||||
assertEquals("""
|
||||
- JobImpl{Active}
|
||||
- "Project Activity":supervisor:ChildScope{Active}
|
||||
- JobImpl{Active}
|
||||
- CIRCULAR REFERENCE: "Project Activity":supervisor:ChildScope{Active}
|
||||
""", dumpCoroutines(projectScope, true, true))
|
||||
assertEquals("""
|
||||
- JobImpl{Active}
|
||||
- "Project Activity":supervisor:ChildScope{Active}
|
||||
- CIRCULAR REFERENCE: JobImpl{Active}
|
||||
""", dumpCoroutines(pluginScope, true, true))
|
||||
projectScope.cancel()
|
||||
pluginScope.cancel()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user