From 347f69b1fd5bb1692b06bb937b6512937faad4e9 Mon Sep 17 00:00:00 2001 From: Sergey Rostov Date: Fri, 1 May 2020 15:47:37 +0300 Subject: [PATCH] ScriptConfigurationManager: rebuild cache synchronously from default loader --- .../CompositeScriptConfigurationManager.kt | 15 +++-- .../configuration/DefaultScriptingSupport.kt | 12 +++- .../utils/DefaultBackgroundExecutor.kt | 10 +++- .../utils/ScriptClassRootsUpdater.kt | 58 +++++++++++++------ .../GradleKotlinScriptConfigurationInputs.kt | 11 +++- 5 files changed, 73 insertions(+), 33 deletions(-) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt index 4a70dddcd7d..3e79f95b737 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.idea.core.script.configuration import com.intellij.ProjectTopics +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.ModuleRootEvent @@ -111,13 +113,16 @@ class CompositeScriptConfigurationManager(val project: Project) : ScriptConfigur * Loads script configuration if classpath roots don't contain [file] yet */ private fun getActualClasspathRoots(file: VirtualFile): ScriptClassRootsCache { - val classpathRoots = classpathRoots - if (classpathRoots.contains(file)) { - return classpathRoots + try { + // we should run default loader if this [file] is not cached in [classpathRoots] + // and it is not supported by any of [plugins] + // getOrLoadConfiguration will do this + // (despite that it's result becomes unused, it still may populate [classpathRoots]) + getOrLoadConfiguration(file, null) + } catch (cancelled: ProcessCanceledException) { + // read actions may be cancelled if we are called by impatient reader } - getOrLoadConfiguration(file) - return this.classpathRoots } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptingSupport.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptingSupport.kt index 4b007100f6b..8a9e4cd1e39 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptingSupport.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/DefaultScriptingSupport.kt @@ -223,7 +223,7 @@ class DefaultScriptingSupport(manager: CompositeScriptConfigurationManager) : De file.removeScriptDependenciesNotificationPanel(project) } saveReports(file, newResult.reports) - setAppliedConfiguration(file, newResult) + setAppliedConfiguration(file, newResult, syncUpdate = true) } else { debug(file) { "configuration changed, notification is shown: old = $oldConfiguration, new = $newConfiguration" @@ -377,15 +377,21 @@ abstract class DefaultScriptingSupportBase(val manager: CompositeScriptConfigura protected open fun setAppliedConfiguration( file: VirtualFile, - newConfigurationSnapshot: ScriptConfigurationSnapshot? + newConfigurationSnapshot: ScriptConfigurationSnapshot?, + syncUpdate: Boolean = false ) { manager.updater.checkInTransaction() val newConfiguration = newConfigurationSnapshot?.configuration debug(file) { "configuration changed = $newConfiguration" } if (newConfiguration != null) { - manager.updater.invalidate(file) cache.setApplied(file, newConfigurationSnapshot) + + if (syncUpdate) { + manager.updater.updateSynchronously() + } else { + manager.updater.invalidate(file) + } } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/DefaultBackgroundExecutor.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/DefaultBackgroundExecutor.kt index d6ca4347e0c..5bac5408a61 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/DefaultBackgroundExecutor.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/DefaultBackgroundExecutor.kt @@ -15,6 +15,7 @@ import com.intellij.util.containers.HashSetQueue import org.jetbrains.kotlin.idea.core.script.debug import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle import java.util.* +import javax.swing.SwingUtilities /** * Sequentially loads script configuration in background. @@ -227,9 +228,12 @@ internal class DefaultBackgroundExecutor( override fun start() { super.start() - BackgroundTaskUtil.executeOnPooledThread(project, Runnable { - run() - }) + // executeOnPooledThread requires read lock, and we may fail to acquire it + SwingUtilities.invokeLater { + BackgroundTaskUtil.executeOnPooledThread(project, { + run() + }) + } } override fun close() { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsUpdater.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsUpdater.kt index f529db87874..f1a9093feec 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsUpdater.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/ScriptClassRootsUpdater.kt @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupportHelpe import org.jetbrains.kotlin.idea.core.script.debug import org.jetbrains.kotlin.idea.util.application.runWriteAction import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock /** * Utility for postponing indexing of new roots to the end of some bulk operation. @@ -78,6 +80,7 @@ class ScriptClassRootsUpdater( ensureUpdateScheduled() } + private val syncLock = ReentrantLock() private var scheduledUpdate: ProgressIndicator? = null @Synchronized @@ -88,31 +91,48 @@ class ScriptClassRootsUpdater( } } - fun doUpdate() { - val updates = manager.collectRootsAndCheckNew() - - if (!updates.changed) return - - ProgressManager.checkCanceled() - - if (updates.hasNewRoots) { - notifyRootsChanged() + /** + * Used from legacy FS cache only, don't use + */ + @Synchronized + fun updateSynchronously() { + syncLock.withLock { + scheduledUpdate?.cancel() + doUpdate(false) } + } - PsiElementFinder.EP.findExtensionOrFail(KotlinScriptDependenciesClassFinder::class.java, project) - .clearCache() + fun doUpdate(underProgressManager: Boolean = true) { + syncLock.withLock { + val updates = manager.collectRootsAndCheckNew() - ScriptDependenciesModificationTracker.getInstance(project).incModificationCount() + if (!updates.changed) return - if (updates.updatedScripts.isNotEmpty()) { - ScriptingSupportHelper.updateHighlighting(project) { - it.path in updates.updatedScripts + try { + ProgressManager.checkCanceled() + + if (updates.hasNewRoots) { + notifyRootsChanged() + } + + PsiElementFinder.EP.findExtensionOrFail(KotlinScriptDependenciesClassFinder::class.java, project) + .clearCache() + + ScriptDependenciesModificationTracker.getInstance(project).incModificationCount() + + if (updates.updatedScripts.isNotEmpty()) { + ScriptingSupportHelper.updateHighlighting(project) { + it.path in updates.updatedScripts + } + } + } catch (cancel: ProcessCanceledException) { + if (underProgressManager) throw cancel + } finally { + synchronized(this) { + scheduledUpdate = null + } } } - - synchronized(this) { - scheduledUpdate = null - } } private fun notifyRootsChanged() { diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt index ee313aee3d7..9cfb1f51c23 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.idea.scripting.gradle import com.intellij.openapi.components.service +import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs @@ -25,10 +26,14 @@ data class GradleKotlinScriptConfigurationInputs( val inputsTS: Long ) : CachedConfigurationInputs { override fun isUpToDate(project: Project, file: VirtualFile, ktFile: KtFile?): Boolean { - val actualStamp = getGradleScriptInputsStamp(project, file, ktFile) ?: return false + try { + val actualStamp = getGradleScriptInputsStamp(project, file, ktFile) ?: return false - if (actualStamp.sections != this.sections) return false + if (actualStamp.sections != this.sections) return false - return project.service().areRelatedFilesUpToDate(file, inputsTS) + return project.service().areRelatedFilesUpToDate(file, inputsTS) + } catch (cancel: ProcessCanceledException) { + return false + } } } \ No newline at end of file