From e9d86d402a6b443e46d97529cb0c254718f9c53c Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Fri, 5 May 2017 23:18:09 +0300 Subject: [PATCH] PY-24068: Do not call ``copyFromLegacy`` from F/J thread ``copyFromLegacy()`` copies data from old configuration to new one. It uses ``PsiDocumentManager`` so it needs to post tasks to EDT using invoke-n-wait. Since this method is called from ``readExternal()`` it is accessed from F/J thread under read action that clones template configuration to check if gutter has to be displayed. Calling invokeAndWait under read action causes dead lock. We now convert old template configurations to new one using project event that takes place on EDT as we do for regular configurations and explicitly check coping process takes places on EDT. This case is rare since no one really needs to have script or file set in default configuration (and working directory does not need ``PsiDocumentManager``) --- .../python/testing/PyTestLegacyInterop.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/python/src/com/jetbrains/python/testing/PyTestLegacyInterop.kt b/python/src/com/jetbrains/python/testing/PyTestLegacyInterop.kt index eea7a1350d75..90263758e806 100644 --- a/python/src/com/jetbrains/python/testing/PyTestLegacyInterop.kt +++ b/python/src/com/jetbrains/python/testing/PyTestLegacyInterop.kt @@ -15,6 +15,7 @@ */ package com.jetbrains.python.testing +import com.google.common.base.Preconditions import com.intellij.execution.RunManager import com.intellij.execution.actions.RunConfigurationProducer import com.intellij.execution.configurations.RunConfiguration @@ -41,6 +42,7 @@ import com.jetbrains.python.testing.nosetestLegacy.PythonNoseTestRunConfiguratio import com.jetbrains.python.testing.pytestLegacy.PyTestRunConfiguration import com.jetbrains.python.testing.unittestLegacy.PythonUnitTestRunConfiguration import org.jdom.Element +import javax.swing.SwingUtilities /** * Module to support legacy configurations. @@ -84,7 +86,10 @@ class PyTestLegacyInteropInitializer { */ private fun projectInitialized(project: Project) { assert(project.isInitialized, { "Project is not initialized yet" }) - RunManager.getInstance(project).allConfigurationsList.filterIsInstance(PyAbstractTestConfiguration::class.java).forEach { + + val manager = RunManager.getInstance(project) + val configurations = factories.map { manager.getConfigurationTemplate(it) } + manager.allConfigurationsList + configurations.filterIsInstance(PyAbstractTestConfiguration::class.java).forEach { it.legacyConfigurationAdapter.copyFromLegacyIfNeeded() } } @@ -196,10 +201,10 @@ class PyTestLegacyConfigurationAdapter(newCo fun copyFromLegacyIfNeeded() { assert(project.isInitialized, { "Initialized project required" }) - if (containsLegacyInformation ?: return && !(legacyInformationCopiedToNew ?: false)) { - configManager.copyFromLegacy() - legacyInformationCopiedToNew = true - } + if (containsLegacyInformation ?: return && !(legacyInformationCopiedToNew ?: false) && SwingUtilities.isEventDispatchThread()) { + configManager.copyFromLegacy() + legacyInformationCopiedToNew = true + } } } @@ -239,10 +244,13 @@ private abstract class LegacyConfigurationManager< fun isLoaded() = getFieldsToCheckForEmptiness().find { !it.isNullOrBlank() } != null /** + * This method should be called from AWT thread only + * * Copies config from legacy to new configuration. * Used by all runners but py.test which has very different settings */ open fun copyFromLegacy() { + Preconditions.checkState(SwingUtilities.isEventDispatchThread(), "Run on AWT thread only") when (legacyConfig.testType) { TestType.TEST_CLASS, TestType.TEST_FUNCTION, TestType.TEST_METHOD -> { val virtualFile = getVirtualFileByPath(legacyConfig.scriptName) ?: return