Fixed locking granularity in ScriptDefinitionsManager

#KT-34552 Fixed
#KT-39547 Fixed
This commit is contained in:
Vladimir Dolzhenko
2020-07-07 19:15:32 +00:00
parent 77ccb3767c
commit 45600830d3
2 changed files with 39 additions and 31 deletions
@@ -16,7 +16,6 @@ import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.progress.runBackgroundableTask
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ex.PathUtilEx import com.intellij.openapi.projectRoots.ex.PathUtilEx
@@ -76,6 +75,8 @@ class LoadScriptDefinitionsStartupActivity : StartupActivity {
class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() { class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() {
private var definitionsBySource = mutableMapOf<ScriptDefinitionsSource, List<ScriptDefinition>>() private var definitionsBySource = mutableMapOf<ScriptDefinitionsSource, List<ScriptDefinition>>()
@Volatile
private var definitions: List<ScriptDefinition>? = null private var definitions: List<ScriptDefinition>? = null
private val failedContributorsHashes = HashSet<Int>() private val failedContributorsHashes = HashSet<Int>()
@@ -121,21 +122,26 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = findDefinition(File(fileName).toScriptSource())?.legacyDefinition override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = findDefinition(File(fileName).toScriptSource())?.legacyDefinition
fun reloadDefinitionsBy(source: ScriptDefinitionsSource) = lock.write { fun reloadDefinitionsBy(source: ScriptDefinitionsSource) {
if (definitions == null) return // not loaded yet if (definitions == null) return // not loaded yet
if (source !in definitionsBySource) error("Unknown script definition source: $source") lock.write {
if (source !in definitionsBySource) error("Unknown script definition source: $source")
}
definitionsBySource[source] = source.safeGetDefinitions() val safeGetDefinitions = source.safeGetDefinitions()
lock.write {
definitionsBySource[source] = safeGetDefinitions
definitions = definitionsBySource.values.flattenTo(mutableListOf()) definitions = definitionsBySource.values.flattenTo(mutableListOf())
updateDefinitions() updateDefinitions()
}
} }
override val currentDefinitions override val currentDefinitions
get() = get() =
(definitions ?: kotlin.run { (definitions ?: run {
reloadScriptDefinitions() reloadScriptDefinitions()
definitions!! definitions!!
}).asSequence().filter { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) } }).asSequence().filter { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) }
@@ -149,45 +155,46 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
return fromNewEp.dropLast(1) + fromDeprecatedEP + fromNewEp.last() return fromNewEp.dropLast(1) + fromDeprecatedEP + fromNewEp.last()
} }
fun reloadScriptDefinitionsIfNeeded() = lock.write { fun reloadScriptDefinitionsIfNeeded() {
if (definitions == null) { definitions ?: loadScriptDefinitions()
loadScriptDefinitions()
}
} }
fun reloadScriptDefinitions() = lock.write { fun reloadScriptDefinitions() = loadScriptDefinitions()
loadScriptDefinitions()
}
private fun loadScriptDefinitions() { private fun loadScriptDefinitions() {
for (source in getSources()) { val newDefinitionsBySource = getSources().map { it to it.safeGetDefinitions() }.toMap()
val definitions = source.safeGetDefinitions()
definitionsBySource[source] = definitions lock.write {
definitionsBySource.putAll(newDefinitionsBySource)
definitions = definitionsBySource.values.flattenTo(mutableListOf())
updateDefinitions()
} }
definitions = definitionsBySource.values.flattenTo(mutableListOf())
updateDefinitions()
} }
fun reorderScriptDefinitions() = lock.write { fun reorderScriptDefinitions() {
definitions?.forEach { definitions?.forEach {
it.order = KotlinScriptingSettings.getInstance(project).getScriptDefinitionOrder(it) val order = KotlinScriptingSettings.getInstance(project).getScriptDefinitionOrder(it)
lock.write {
it.order = order
}
} }
definitions = definitions?.sortedBy { it.order } lock.write {
definitions = definitions?.sortedBy { it.order }
updateDefinitions() updateDefinitions()
}
} }
fun getAllDefinitions(): List<ScriptDefinition> { fun getAllDefinitions(): List<ScriptDefinition> = definitions ?: run {
return definitions ?: kotlin.run { reloadScriptDefinitions()
reloadScriptDefinitions() definitions!!
definitions!!
}
} }
fun isReady(): Boolean { fun isReady(): Boolean {
return definitions != null && definitionsBySource.keys.all { source -> if (definitions == null) return false
val keys = lock.write { definitionsBySource.keys }
return keys.all { source ->
// TODO: implement another API for readiness checking // TODO: implement another API for readiness checking
(source as? ScriptDefinitionContributor)?.isReady() != false (source as? ScriptDefinitionContributor)?.isReady() != false
} }
@@ -78,6 +78,7 @@ class ScriptTemplatesFromDependenciesProvider(private val project: Project) : Sc
} }
} }
@Volatile
private var _definitions: List<ScriptDefinition>? = null private var _definitions: List<ScriptDefinition>? = null
private val definitionsLock = ReentrantLock() private val definitionsLock = ReentrantLock()