Split script configuration state into applied and loaded to make markUpToDate more clear

This commit is contained in:
Sergey Rostov
2019-11-04 13:35:12 +03:00
committed by Natalia Selezneva
parent fde1d3fdf7
commit fbb2b260dd
5 changed files with 65 additions and 47 deletions
@@ -20,9 +20,9 @@ import kotlinx.coroutines.launch
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.core.script.*
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companion.toVfsRoots
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationCache
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationState
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsIndexer
@@ -40,7 +40,7 @@ import kotlin.concurrent.withLock
* Among this two methods concrete implementation should provide script changes listening (by calling [updater] on some event).
*
* Basically all requests routed to [cache]. If there is no entry in [cache] or it is considered out-of-date,
* then [reloadOutOfDateConfiguration] will be called, which, in turn, should call [saveChangedConfiguration]
* then [reloadOutOfDateConfiguration] will be called, which, in turn, should call [setAppliedConfiguration]
* immediately or in some future (e.g. after user will click "apply context" or/and configuration will
* be calculated by some background thread).
*
@@ -61,7 +61,7 @@ internal abstract class AbstractScriptConfigurationManager(
/**
* Will be called on [cache] miss or when [file] is changed.
* Implementation should initiate loading of [file]'s script configuration and call [saveChangedConfiguration]
* Implementation should initiate loading of [file]'s script configuration and call [setAppliedConfiguration]
* immediately or in some future
* (e.g. after user will click "apply context" or/and configuration will be calculated by some background thread).
*
@@ -73,7 +73,7 @@ internal abstract class AbstractScriptConfigurationManager(
*/
protected abstract fun reloadOutOfDateConfiguration(
file: KtFile,
isFirstLoad: Boolean = getCachedConfiguration(file.originalFile.virtualFile) == null,
isFirstLoad: Boolean = getAppliedConfiguration(file.originalFile.virtualFile) == null,
loadEvenWillNotBeApplied: Boolean = false,
forceSync: Boolean = false
)
@@ -87,13 +87,16 @@ internal abstract class AbstractScriptConfigurationManager(
override fun getScriptClasspath(file: KtFile): List<VirtualFile> =
toVfsRoots(getConfiguration(file)?.dependenciesClassPath.orEmpty())
fun getCachedConfiguration(file: VirtualFile?): ScriptConfigurationSnapshot? {
fun getCachedConfigurationState(file: VirtualFile?): ScriptConfigurationState? {
if (file == null) return null
return cache[file]
}
fun getAppliedConfiguration(file: VirtualFile?): ScriptConfigurationSnapshot? =
getCachedConfigurationState(file)?.applied
override fun hasConfiguration(file: KtFile): Boolean {
return getCachedConfiguration(file.originalFile.virtualFile) != null
return getAppliedConfiguration(file.originalFile.virtualFile) != null
}
override fun getConfiguration(file: KtFile): ScriptCompilationConfigurationWrapper? {
@@ -104,7 +107,7 @@ internal abstract class AbstractScriptConfigurationManager(
virtualFile: VirtualFile,
preloadedKtFile: KtFile? = null
): ScriptCompilationConfigurationWrapper? {
val cached = getCachedConfiguration(virtualFile)
val cached = getAppliedConfiguration(virtualFile)
if (cached != null) return cached.configuration
val ktFile = project.getKtFile(virtualFile, preloadedKtFile) ?: return null
@@ -112,7 +115,7 @@ internal abstract class AbstractScriptConfigurationManager(
reloadOutOfDateConfiguration(ktFile, isFirstLoad = true)
}
return getCachedConfiguration(virtualFile)?.configuration
return getAppliedConfiguration(virtualFile)?.configuration
}
override val updater: ScriptConfigurationUpdater = object : ScriptConfigurationUpdater {
@@ -143,7 +146,7 @@ internal abstract class AbstractScriptConfigurationManager(
val virtualFile = file.originalFile.virtualFile
if (virtualFile != null) {
val state = cache[virtualFile]
if (state == null || !state.inputs.isUpToDate(project, virtualFile, file)) {
if (state == null || !state.isUpToDate(project, virtualFile, file)) {
upToDate = false
reloadOutOfDateConfiguration(
file,
@@ -165,14 +168,14 @@ internal abstract class AbstractScriptConfigurationManager(
file as? KtFile ?: error("PsiFile $file should be a KtFile, otherwise script dependencies cannot be loaded")
val virtualFile = file.virtualFile
if (cache[virtualFile]?.inputs?.isUpToDate(project, virtualFile, file) == true) return
if (cache[virtualFile]?.isUpToDate(project, virtualFile, file) == true) return
rootsIndexer.transaction {
reloadOutOfDateConfiguration(file, isFirstLoad = true, forceSync = true)
}
}
protected open fun saveChangedConfiguration(
protected open fun setAppliedConfiguration(
file: VirtualFile,
newConfigurationSnapshot: ScriptConfigurationSnapshot?
) {
@@ -185,7 +188,7 @@ internal abstract class AbstractScriptConfigurationManager(
rootsIndexer.markNewRoot(file, newConfiguration)
}
cache[file] = newConfigurationSnapshot
cache.setApplied(file, newConfigurationSnapshot)
clearClassRootsCaches()
}
@@ -193,11 +196,11 @@ internal abstract class AbstractScriptConfigurationManager(
updateHighlighting(listOf(file))
}
protected fun markUpToDate(
protected fun setLoadedConfiguration(
file: VirtualFile,
inputs: CachedConfigurationInputs
configurationSnapshot: ScriptConfigurationSnapshot
) {
cache.markUpToDate(file, inputs)
cache.setLoaded(file, configurationSnapshot)
}
private fun hasNotCachedRoots(configuration: ScriptCompilationConfigurationWrapper): Boolean {
@@ -141,7 +141,7 @@ internal class DefaultScriptConfigurationManager(project: Project) :
* and file will be up-to-date checked again. This will happen after current loading,
* because only `backgroundExecutor` execute tasks in one thread.
* - `invalid, waiting for apply`:
* Loading will not be queued, since we are marking file is up to date with
* Loading will not be queued, since we are marking file as up-to-date with
* not yet applied configuration.
*
* Sync:
@@ -177,8 +177,8 @@ internal class DefaultScriptConfigurationManager(project: Project) :
backgroundExecutor.ensureScheduled(virtualFile) {
// don't start loading if nothing was changed
// (in case we checking for up-to-date and loading concurrently)
val cached = getCachedConfiguration(virtualFile)
if (cached == null || !cached.inputs.isUpToDate(project, virtualFile)) {
val cached = getCachedConfigurationState(virtualFile)
if (cached == null || !cached.isUpToDate(project, virtualFile)) {
val actualIsFirstLoad = cached == null
async.first { it.loadDependencies(actualIsFirstLoad, virtualFile, scriptDefinition, loadingContext) }
}
@@ -202,7 +202,7 @@ internal class DefaultScriptConfigurationManager(project: Project) :
private val loadingContext = object : ScriptConfigurationLoadingContext {
override fun getCachedConfiguration(file: VirtualFile): ScriptConfigurationSnapshot? =
this@DefaultScriptConfigurationManager.getCachedConfiguration(file)
this@DefaultScriptConfigurationManager.getAppliedConfiguration(file)
override fun suggestNewConfiguration(file: VirtualFile, newResult: ScriptConfigurationSnapshot) {
suggestOrSaveConfiguration(file, newResult, false)
@@ -221,13 +221,13 @@ internal class DefaultScriptConfigurationManager(project: Project) :
saveLock.withLock {
debug(file) { "configuration received = $newResult" }
markUpToDate(file, newResult.inputs)
setLoadedConfiguration(file, newResult)
val newConfiguration = newResult.configuration
if (newConfiguration == null) {
saveReports(file, newResult.reports)
} else {
val oldConfiguration = getCachedConfiguration(file)?.configuration
val oldConfiguration = getAppliedConfiguration(file)?.configuration
if (oldConfiguration == newConfiguration) {
saveReports(file, newResult.reports)
file.removeScriptDependenciesNotificationPanel(project)
@@ -242,7 +242,7 @@ internal class DefaultScriptConfigurationManager(project: Project) :
file.removeScriptDependenciesNotificationPanel(project)
}
saveReports(file, newResult.reports)
saveChangedConfiguration(file, newResult)
setAppliedConfiguration(file, newResult)
} else {
debug(file) {
"configuration changed, notification is shown: old = $oldConfiguration, new = $newConfiguration"
@@ -253,7 +253,7 @@ internal class DefaultScriptConfigurationManager(project: Project) :
saveReports(file, newResult.reports)
file.removeScriptDependenciesNotificationPanel(project)
rootsIndexer.transaction {
saveChangedConfiguration(file, newResult)
setAppliedConfiguration(file, newResult)
}
}
)
@@ -30,6 +30,14 @@ data class ScriptConfigurationSnapshot(
val configuration: ScriptCompilationConfigurationWrapper?
)
data class ScriptConfigurationState(
val applied: ScriptConfigurationSnapshot? = null,
val loaded: ScriptConfigurationSnapshot? = null
) {
fun isUpToDate(project: Project, file: VirtualFile, ktFile: KtFile? = null): Boolean =
(loaded ?: applied)?.inputs?.isUpToDate(project, file, ktFile) ?: false
}
/**
* Cached configurations for file's specific snapshot state.
*
@@ -37,11 +45,11 @@ data class ScriptConfigurationSnapshot(
* Reader may do up-to-date check for existed entry.
*/
interface ScriptConfigurationCache {
operator fun get(file: VirtualFile): ScriptConfigurationSnapshot?
operator fun set(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot)
operator fun get(file: VirtualFile): ScriptConfigurationState?
fun markUpToDate(file: VirtualFile, inputs: CachedConfigurationInputs)
fun markOutOfDate(file: VirtualFile) = markUpToDate(file, CachedConfigurationInputs.OutOfDate)
fun setApplied(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot)
fun setLoaded(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot)
fun markOutOfDate(file: VirtualFile)
fun all(): Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>>
fun allApplied(): Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>>
}
@@ -17,34 +17,41 @@ class ScriptConfigurationMemoryCache(
const val MAX_SCRIPTS_CACHED = 50
}
private val memoryCache = SLRUMap<VirtualFile, ScriptConfigurationSnapshot>(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED)
private val memoryCache = SLRUMap<VirtualFile, ScriptConfigurationState>(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED)
@Synchronized
override operator fun get(file: VirtualFile): ScriptConfigurationSnapshot? {
override operator fun get(file: VirtualFile): ScriptConfigurationState? {
return memoryCache.get(file)
}
@Synchronized
override operator fun set(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) {
memoryCache.put(
file,
configurationSnapshot
)
override fun setApplied(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) {
val old = memoryCache[file] ?: ScriptConfigurationState()
memoryCache.put(file, old.copy(applied = configurationSnapshot))
}
override fun markUpToDate(file: VirtualFile, inputs: CachedConfigurationInputs) {
val old = memoryCache[file]
if (old != null) {
memoryCache.put(file, old.copy(inputs = inputs))
}
@Synchronized
override fun setLoaded(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot) {
val old = memoryCache[file] ?: ScriptConfigurationState()
memoryCache.put(file, old.copy(loaded = configurationSnapshot))
}
@Synchronized
override fun markOutOfDate(file: VirtualFile) {
val old = memoryCache[file] ?: ScriptConfigurationState()
memoryCache.put(
file, old.copy(
applied = old.applied?.copy(inputs = CachedConfigurationInputs.OutOfDate),
loaded = old.loaded?.copy(inputs = CachedConfigurationInputs.OutOfDate)
)
)
}
@Synchronized
@Suppress("UNCHECKED_CAST")
override fun all() =
memoryCache.entrySet().map {
if (it.value.configuration == null) null
else it.key to it.value.configuration
override fun allApplied() =
memoryCache.entrySet().mapNotNull {
if (it.value.applied?.configuration == null) null
else it.key to it.value.applied?.configuration
} as Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>>
}
@@ -25,8 +25,8 @@ internal class ScriptClassRootsCache(
private val project: Project,
private val cache: ScriptConfigurationCache
) {
private val all: Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>> get() = cache.all()
private fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? = cache[file]?.configuration
private val all: Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>> get() = cache.allApplied()
private fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? = cache[file]?.applied?.configuration
private fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? {
// workaround for mismatched gradle wrapper and plugin version