Problem: workaround thread leaking problem

This commit is contained in:
Nikolay Krasko
2016-09-09 16:44:47 +03:00
parent 835474e5a1
commit ea1aac07ec
8 changed files with 101 additions and 30 deletions
@@ -63,7 +63,7 @@ public class KotlinConfidenceTest extends LightCompletionTestCase {
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
TestUtilsKt.unInvalidateBuiltinsAndStdLib(getProject(), new RunnableWithException() { TestUtilsKt.doKotlinTearDown(getProject(), new RunnableWithException() {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
KotlinConfidenceTest.super.tearDown(); KotlinConfidenceTest.super.tearDown();
@@ -72,7 +72,7 @@ abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFix
KotlinInternalMode.enabled = kotlinInternalModeOriginalValue KotlinInternalMode.enabled = kotlinInternalModeOriginalValue
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()) VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
unInvalidateBuiltinsAndStdLib(project) { doKotlinTearDown(project) {
super.tearDown() super.tearDown()
} }
@@ -40,7 +40,7 @@ abstract class KotlinLightPlatformCodeInsightFixtureTestCase: LightPlatformCodeI
KotlinInternalMode.enabled = kotlinInternalModeOriginalValue KotlinInternalMode.enabled = kotlinInternalModeOriginalValue
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()) VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
unInvalidateBuiltinsAndStdLib(project) { doKotlinTearDown(project) {
super.tearDown() super.tearDown()
} }
} }
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.test package org.jetbrains.kotlin.idea.test
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Document
import com.intellij.openapi.module.Module 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.file.impl.FileManagerImpl
import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.psi.impl.source.PsiFileImpl
import com.intellij.testFramework.LightPlatformTestCase import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.ThreadTracker
import com.intellij.util.Consumer import com.intellij.util.Consumer
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages 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.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import java.lang.IllegalArgumentException
import java.util.* import java.util.*
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.atomic.AtomicBoolean
enum class ModuleKind { enum class ModuleKind {
KOTLIN_JVM_WITH_STDLIB_SOURCES, KOTLIN_JVM_WITH_STDLIB_SOURCES,
@@ -47,18 +52,16 @@ enum class ModuleKind {
fun Module.configureAs(descriptor: KotlinLightProjectDescriptor) { fun Module.configureAs(descriptor: KotlinLightProjectDescriptor) {
val module = this val module = this
updateModel(module, object : Consumer<ModifiableRootModel> { updateModel(module, Consumer<ModifiableRootModel> { model ->
override fun consume(model: ModifiableRootModel) { if (descriptor.sdk != null) {
if (descriptor.sdk != null) { model.sdk = descriptor.sdk
model.sdk = descriptor.sdk }
} val entries = model.contentEntries
val entries = model.contentEntries if (entries.isEmpty()) {
if (entries.isEmpty()) { descriptor.configureModule(module, model)
descriptor.configureModule(module, model) }
} else {
else { descriptor.configureModule(module, model, entries[0])
descriptor.configureModule(module, model, entries[0])
}
} }
}) })
} }
@@ -76,17 +79,29 @@ fun Module.configureAs(kind: ModuleKind) {
} }
fun KtFile.dumpTextWithErrors(): String { fun KtFile.dumpTextWithErrors(): String {
val diagnostics = analyzeFullyAndGetResult().bindingContext.getDiagnostics() val diagnostics = analyzeFullyAndGetResult().bindingContext.diagnostics
val errors = diagnostics.filter { it.getSeverity() == Severity.ERROR } val errors = diagnostics.filter { it.severity == Severity.ERROR }
if (errors.isEmpty()) return text if (errors.isEmpty()) return text
val header = errors.map { "// ERROR: " + DefaultErrorMessages.render(it).replace('\n', ' ') }.joinToString("\n", postfix = "\n") val header = errors.map { "// ERROR: " + DefaultErrorMessages.render(it).replace('\n', ' ') }.joinToString("\n", postfix = "\n")
return header + text return header + text
} }
fun closeAndDeleteProject(): Unit = 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<KotlinDecompiledFileViewProvider>() val stdLibViewProviders = HashSet<KotlinDecompiledFileViewProvider>()
val vFileToViewProviderMap = ((PsiManager.getInstance(project) as PsiManagerEx).fileManager as FileManagerImpl).vFileToViewProviderMap val vFileToViewProviderMap = ((PsiManager.getInstance(project) as PsiManagerEx).fileManager as FileManagerImpl).vFileToViewProviderMap
for ((file, viewProvider) in 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. // Base tearDown() invalidates builtins and std-lib files. Restore them with brute force.
fun unInvalidateFile(file: PsiFileImpl) { fun unInvalidateFile(file: PsiFileImpl) {
@@ -106,23 +121,19 @@ fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: RunnableWithExcept
stdLibViewProviders.forEach { stdLibViewProviders.forEach {
it.allFiles.forEach { unInvalidateFile(it as KtDecompiledFile) } 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") 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) { fun invalidateLibraryCache(project: Project) {
LibraryModificationTracker.getInstance(project).incModificationCount() LibraryModificationTracker.getInstance(project).incModificationCount()
} }
fun Document.extractMarkerOffset(project: Project, caretMarker: String = "<caret>"): Int { fun Document.extractMarkerOffset(project: Project, caretMarker: String = "<caret>"): Int {
val offset = runWriteAction { val offset = runWriteAction {
val text = StringBuilder(getText()) val text = StringBuilder(text)
val offset = text.indexOf(caretMarker) val offset = text.indexOf(caretMarker)
if (offset >= 0) { if (offset >= 0) {
@@ -138,3 +149,48 @@ fun Document.extractMarkerOffset(project: Project, caretMarker: String = "<caret
return offset return offset
} }
private val patched = AtomicBoolean(false)
fun patchThreadTracker(runnable: RunnableWithException) {
patchThreadTracker {
runnable.run()
}
}
fun patchThreadTracker(runnable: () -> 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<String>
wellKnownOffenders.add("ForkJoinPool.commonPool-worker-")
}
catch (e: NoSuchFieldException) {
println("Patching failed: " + e)
}
catch (e: IllegalAccessException) {
println("Patching failed: " + e)
}
}
patch()
runnable()
}
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.idea;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase; import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; 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; import org.jetbrains.kotlin.test.KotlinTestUtils;
abstract public class KotlinDaemonAnalyzerTestCase extends DaemonAnalyzerTestCase { abstract public class KotlinDaemonAnalyzerTestCase extends DaemonAnalyzerTestCase {
@@ -29,7 +31,12 @@ abstract public class KotlinDaemonAnalyzerTestCase extends DaemonAnalyzerTestCas
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
super.tearDown(); TestUtilsKt.patchThreadTracker(new RunnableWithException() {
@Override
public void run() throws Exception {
KotlinDaemonAnalyzerTestCase.super.tearDown();
}
});
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()); VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory());
} }
} }
@@ -34,7 +34,7 @@ abstract public class KotlinLightQuickFixTestCase extends LightQuickFixTestCase
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()); VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory());
TestUtilsKt.unInvalidateBuiltinsAndStdLib(getProject(), new RunnableWithException() { TestUtilsKt.doKotlinTearDown(getProject(), new RunnableWithException() {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
KotlinLightQuickFixTestCase.super.tearDown(); KotlinLightQuickFixTestCase.super.tearDown();
@@ -45,6 +45,8 @@ import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime; import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil; import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; 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.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
@@ -99,6 +101,7 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase {
ConfigLibraryUtil.addLibrary(customLibEditor, model); ConfigLibraryUtil.addLibrary(customLibEditor, model);
} }
@SuppressWarnings("MethodDoesntCallSuperMethod")
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
if (getTestName(true).startsWith("dex")) { 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()); VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory());
} }
@@ -72,7 +72,7 @@ public abstract class KotlinAndroidTestCaseBase extends UsefulTestCase {
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
TestUtilsKt.unInvalidateBuiltinsAndStdLib(getProject(), new RunnableWithException() { TestUtilsKt.doKotlinTearDown(getProject(), new RunnableWithException() {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
KotlinAndroidTestCaseBase.super.tearDown(); KotlinAndroidTestCaseBase.super.tearDown();