Add basic synchronization workaround for scratch

This commit is contained in:
Mikhail Bogdanov
2020-03-19 08:36:40 +01:00
parent dc18c62dbc
commit 64a4d09bb3
4 changed files with 29 additions and 5 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.scratch package org.jetbrains.kotlin.idea.scratch
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.application.runReadAction import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.editor.EditorFactory
@@ -188,6 +189,7 @@ abstract class SequentialScratchExecutor(file: ScratchFile) : ScratchExecutor(fi
stopExecution { stopExecution {
lock.release() lock.release()
} }
// blocking UI thread!?
check(lock.tryAcquire(2, TimeUnit.SECONDS)) { check(lock.tryAcquire(2, TimeUnit.SECONDS)) {
"Couldn't stop REPL process in 2 seconds" "Couldn't stop REPL process in 2 seconds"
} }
@@ -29,6 +29,8 @@ import javax.xml.parsers.DocumentBuilderFactory
class KtScratchReplExecutor(file: ScratchFile) : SequentialScratchExecutor(file) { class KtScratchReplExecutor(file: ScratchFile) : SequentialScratchExecutor(file) {
private val history: ReplHistory = ReplHistory() private val history: ReplHistory = ReplHistory()
@Volatile
private var osProcessHandler: OSProcessHandler? = null private var osProcessHandler: OSProcessHandler? = null
override fun startExecution() { override fun startExecution() {
@@ -65,6 +67,8 @@ class KtScratchReplExecutor(file: ScratchFile) : SequentialScratchExecutor(file)
} }
} }
// There should be some kind of more wise synchronization cause this method is called from non-UI thread (process handler thread)
// and actually there could be side effects in handlers
private fun clearState() { private fun clearState() {
history.clear() history.clear()
osProcessHandler = null osProcessHandler = null
@@ -145,9 +149,20 @@ class KtScratchReplExecutor(file: ScratchFile) : SequentialScratchExecutor(file)
} }
override fun notifyProcessTerminated(exitCode: Int) { override fun notifyProcessTerminated(exitCode: Int) {
super.notifyProcessTerminated(exitCode) // Do state cleaning before notification otherwise KtScratchFileEditorWithPreview.dispose
// would try to stop process again (after stop in tests 'stopReplProcess`)
// via `stopExecution` (because handler is not null) with next exception:
//
// Caused by: com.intellij.testFramework.LoggedErrorProcessor$TestLoggerAssertionError: The pipe is being closed
// at com.intellij.testFramework.LoggedErrorProcessor.processError(LoggedErrorProcessor.java:66)
// at com.intellij.testFramework.TestLogger.error(TestLogger.java:40)
// at com.intellij.openapi.diagnostic.Logger.error(Logger.java:170)
// at org.jetbrains.kotlin.idea.scratch.ScratchExecutor.errorOccurs(ScratchExecutor.kt:50)
// at org.jetbrains.kotlin.idea.scratch.repl.KtScratchReplExecutor.stopExecution(KtScratchReplExecutor.kt:61)
// at org.jetbrains.kotlin.idea.scratch.SequentialScratchExecutor.stopExecution$default(ScratchExecutor.kt:90)
clearState() clearState()
super.notifyProcessTerminated(exitCode)
} }
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding))) private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
+1 -1
View File
@@ -45,7 +45,7 @@ sourceSets {
"test" { projectDefault() } "test" { projectDefault() }
} }
projectTest { projectTest(parallel = true) {
dependsOn(":dist") dependsOn(":dist")
workingDir = rootDir workingDir = rootDir
} }
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.actions.KOTLIN_WORKSHEET_EXTENSION import org.jetbrains.kotlin.idea.actions.KOTLIN_WORKSHEET_EXTENSION
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
import org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction import org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction
import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction
@@ -99,7 +100,7 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
) )
} }
val outputDir = createTempDir(dirName) val outputDir = FileUtil.createTempDirectory(dirName, "")
if (javaFiles.isNotEmpty()) { if (javaFiles.isNotEmpty()) {
val options = listOf("-d", outputDir.path) val options = listOf("-d", outputDir.path)
@@ -259,7 +260,11 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
// wait until output is displayed in editor or for 1 minute // wait until output is displayed in editor or for 1 minute
while (ScratchCompilationSupport.isAnyInProgress() && (System.currentTimeMillis() - start) < 60000) { while (ScratchCompilationSupport.isAnyInProgress()) {
if ((System.currentTimeMillis() - start) > TIME_OUT) {
LOG.warn("Waiting timeout $TIME_OUT ms is exceed")
break
}
Thread.sleep(100) Thread.sleep(100)
} }
@@ -323,6 +328,8 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
} }
companion object { companion object {
private const val TIME_OUT = 60000 // 1 min
private val INSTANCE_WITH_KOTLIN_TEST = object : KotlinWithJdkAndRuntimeLightProjectDescriptor( private val INSTANCE_WITH_KOTLIN_TEST = object : KotlinWithJdkAndRuntimeLightProjectDescriptor(
arrayListOf( arrayListOf(
ForTestCompileRuntime.runtimeJarForTests(), ForTestCompileRuntime.runtimeJarForTests(),