From ea1aac07ec2f7b04fbc96d22b45dbff607b18a89 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 9 Sep 2016 16:44:47 +0300 Subject: [PATCH] Problem: workaround thread leaking problem --- .../test/confidence/KotlinConfidenceTest.java | 2 +- .../KotlinLightCodeInsightFixtureTestCase.kt | 2 +- ...LightPlatformCodeInsightFixtureTestCase.kt | 2 +- .../jetbrains/kotlin/idea/test/testUtils.kt | 102 ++++++++++++++---- .../idea/KotlinDaemonAnalyzerTestCase.java | 9 +- .../idea/KotlinLightQuickFixTestCase.java | 2 +- .../idea/debugger/KotlinDebuggerTestCase.java | 10 +- .../android/KotlinAndroidTestCaseBase.java | 2 +- 8 files changed, 101 insertions(+), 30 deletions(-) diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java index 62b178af155..f52899aa558 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java @@ -63,7 +63,7 @@ public class KotlinConfidenceTest extends LightCompletionTestCase { @Override protected void tearDown() throws Exception { - TestUtilsKt.unInvalidateBuiltinsAndStdLib(getProject(), new RunnableWithException() { + TestUtilsKt.doKotlinTearDown(getProject(), new RunnableWithException() { @Override public void run() throws Exception { KotlinConfidenceTest.super.tearDown(); diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt index 70fd8016d22..253b5b5e461 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt @@ -72,7 +72,7 @@ abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFix KotlinInternalMode.enabled = kotlinInternalModeOriginalValue VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()) - unInvalidateBuiltinsAndStdLib(project) { + doKotlinTearDown(project) { super.tearDown() } diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightPlatformCodeInsightFixtureTestCase.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightPlatformCodeInsightFixtureTestCase.kt index d33cc5c8afa..fc2ca98d60f 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightPlatformCodeInsightFixtureTestCase.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinLightPlatformCodeInsightFixtureTestCase.kt @@ -40,7 +40,7 @@ abstract class KotlinLightPlatformCodeInsightFixtureTestCase: LightPlatformCodeI KotlinInternalMode.enabled = kotlinInternalModeOriginalValue VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()) - unInvalidateBuiltinsAndStdLib(project) { + doKotlinTearDown(project) { super.tearDown() } } diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt index bd8753e2bb3..9c261111dc2 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.test +import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Document import com.intellij.openapi.module.Module @@ -29,6 +30,7 @@ import com.intellij.psi.impl.PsiManagerEx import com.intellij.psi.impl.file.impl.FileManagerImpl import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.testFramework.LightPlatformTestCase +import com.intellij.testFramework.ThreadTracker import com.intellij.util.Consumer import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages @@ -38,7 +40,10 @@ import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.psi.KtFile +import java.lang.IllegalArgumentException import java.util.* +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.atomic.AtomicBoolean enum class ModuleKind { KOTLIN_JVM_WITH_STDLIB_SOURCES, @@ -47,18 +52,16 @@ enum class ModuleKind { fun Module.configureAs(descriptor: KotlinLightProjectDescriptor) { val module = this - updateModel(module, object : Consumer { - override fun consume(model: ModifiableRootModel) { - if (descriptor.sdk != null) { - model.sdk = descriptor.sdk - } - val entries = model.contentEntries - if (entries.isEmpty()) { - descriptor.configureModule(module, model) - } - else { - descriptor.configureModule(module, model, entries[0]) - } + updateModel(module, Consumer { model -> + if (descriptor.sdk != null) { + model.sdk = descriptor.sdk + } + val entries = model.contentEntries + if (entries.isEmpty()) { + descriptor.configureModule(module, model) + } + else { + descriptor.configureModule(module, model, entries[0]) } }) } @@ -76,17 +79,29 @@ fun Module.configureAs(kind: ModuleKind) { } fun KtFile.dumpTextWithErrors(): String { - val diagnostics = analyzeFullyAndGetResult().bindingContext.getDiagnostics() - val errors = diagnostics.filter { it.getSeverity() == Severity.ERROR } + val diagnostics = analyzeFullyAndGetResult().bindingContext.diagnostics + val errors = diagnostics.filter { it.severity == Severity.ERROR } if (errors.isEmpty()) return text val header = errors.map { "// ERROR: " + DefaultErrorMessages.render(it).replace('\n', ' ') }.joinToString("\n", postfix = "\n") return header + text } fun closeAndDeleteProject(): Unit = - ApplicationManager.getApplication().runWriteAction() { LightPlatformTestCase.closeAndDeleteProject() } + ApplicationManager.getApplication().runWriteAction { LightPlatformTestCase.closeAndDeleteProject() } -fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: RunnableWithException) { +fun doKotlinTearDown(project: Project, runnable: RunnableWithException) { + doKotlinTearDown(project) { runnable.run() } +} + +fun doKotlinTearDown(project: Project, runnable: () -> Unit) { + unInvalidateBuiltinsAndStdLib(project) { + patchThreadTracker { + runnable() + } + } +} + +fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: () -> Unit) { val stdLibViewProviders = HashSet() val vFileToViewProviderMap = ((PsiManager.getInstance(project) as PsiManagerEx).fileManager as FileManagerImpl).vFileToViewProviderMap for ((file, viewProvider) in vFileToViewProviderMap) { @@ -95,7 +110,7 @@ fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: RunnableWithExcept } } - runnable.run() + runnable() // Base tearDown() invalidates builtins and std-lib files. Restore them with brute force. fun unInvalidateFile(file: PsiFileImpl) { @@ -106,23 +121,19 @@ fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: RunnableWithExcept stdLibViewProviders.forEach { it.allFiles.forEach { unInvalidateFile(it as KtDecompiledFile) } - vFileToViewProviderMap.set(it.virtualFile, it) + vFileToViewProviderMap[it.virtualFile] = it } } private val VirtualFile.isStdLibFile: Boolean get() = presentableUrl.contains("kotlin-runtime.jar") -fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: () -> Unit) { - unInvalidateBuiltinsAndStdLib(project, RunnableWithException { runnable() }) -} - fun invalidateLibraryCache(project: Project) { LibraryModificationTracker.getInstance(project).incModificationCount() } fun Document.extractMarkerOffset(project: Project, caretMarker: String = ""): Int { val offset = runWriteAction { - val text = StringBuilder(getText()) + val text = StringBuilder(text) val offset = text.indexOf(caretMarker) if (offset >= 0) { @@ -137,4 +148,49 @@ fun Document.extractMarkerOffset(project: Project, caretMarker: String = " Unit) { + fun patch() { + if (patched.get()) return + + patched.compareAndSet(false, true) + + IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool() + + // Check setup was successful + val commonPoolFactoryName = ForkJoinPool.commonPool().factory.javaClass.name + if (commonPoolFactoryName == IdeaForkJoinWorkerThreadFactory::class.java.name) { + return + } + + println("Patching!") + + try { + val wellKnownOffendersField = ThreadTracker::class.java.getDeclaredField("wellKnownOffenders") + wellKnownOffendersField.isAccessible = true + + @Suppress("UNCHECKED_CAST") + val wellKnownOffenders = wellKnownOffendersField.get(null) as MutableSet + + wellKnownOffenders.add("ForkJoinPool.commonPool-worker-") + } + catch (e: NoSuchFieldException) { + println("Patching failed: " + e) + } + catch (e: IllegalAccessException) { + println("Patching failed: " + e) + } + } + + patch() + runnable() } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/KotlinDaemonAnalyzerTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/KotlinDaemonAnalyzerTestCase.java index cf9f089406c..573b164a8ca 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/KotlinDaemonAnalyzerTestCase.java +++ b/idea/tests/org/jetbrains/kotlin/idea/KotlinDaemonAnalyzerTestCase.java @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.idea; import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase; import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; +import org.jetbrains.kotlin.idea.test.RunnableWithException; +import org.jetbrains.kotlin.idea.test.TestUtilsKt; import org.jetbrains.kotlin.test.KotlinTestUtils; abstract public class KotlinDaemonAnalyzerTestCase extends DaemonAnalyzerTestCase { @@ -29,7 +31,12 @@ abstract public class KotlinDaemonAnalyzerTestCase extends DaemonAnalyzerTestCas @Override protected void tearDown() throws Exception { - super.tearDown(); + TestUtilsKt.patchThreadTracker(new RunnableWithException() { + @Override + public void run() throws Exception { + KotlinDaemonAnalyzerTestCase.super.tearDown(); + } + }); VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()); } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/KotlinLightQuickFixTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/KotlinLightQuickFixTestCase.java index cbaccf26ba5..29517accd18 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/KotlinLightQuickFixTestCase.java +++ b/idea/tests/org/jetbrains/kotlin/idea/KotlinLightQuickFixTestCase.java @@ -34,7 +34,7 @@ abstract public class KotlinLightQuickFixTestCase extends LightQuickFixTestCase protected void tearDown() throws Exception { VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()); - TestUtilsKt.unInvalidateBuiltinsAndStdLib(getProject(), new RunnableWithException() { + TestUtilsKt.doKotlinTearDown(getProject(), new RunnableWithException() { @Override public void run() throws Exception { KotlinLightQuickFixTestCase.super.tearDown(); diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java index 0aaad0d0ea5..a4b97e81412 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java @@ -45,6 +45,8 @@ import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade; import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime; import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil; import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; +import org.jetbrains.kotlin.idea.test.RunnableWithException; +import org.jetbrains.kotlin.idea.test.TestUtilsKt; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtFile; @@ -99,6 +101,7 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase { ConfigLibraryUtil.addLibrary(customLibEditor, model); } + @SuppressWarnings("MethodDoesntCallSuperMethod") @Override protected void tearDown() throws Exception { if (getTestName(true).startsWith("dex")) { @@ -113,7 +116,12 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase { } }); - super.tearDown(); + TestUtilsKt.patchThreadTracker(new RunnableWithException() { + @Override + public void run() throws Exception { + KotlinDebuggerTestCase.super.tearDown(); + } + }); VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory()); } diff --git a/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/KotlinAndroidTestCaseBase.java b/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/KotlinAndroidTestCaseBase.java index e6ed93c0adc..1832b24b528 100644 --- a/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/KotlinAndroidTestCaseBase.java +++ b/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/KotlinAndroidTestCaseBase.java @@ -72,7 +72,7 @@ public abstract class KotlinAndroidTestCaseBase extends UsefulTestCase { @Override protected void tearDown() throws Exception { - TestUtilsKt.unInvalidateBuiltinsAndStdLib(getProject(), new RunnableWithException() { + TestUtilsKt.doKotlinTearDown(getProject(), new RunnableWithException() { @Override public void run() throws Exception { KotlinAndroidTestCaseBase.super.tearDown();