[Invariant Fix] Use the same lock inside single GlobalFacade
Before refinement, order of accessing compiler's entities (supertypes,
descriptors, memberscopes) was always the following:
sources -> libraries -> SDK (built-ins)
With refinement, it is sometimes possible to inverse this order, e.g.
access sources after libraries or SDK. This mainly happens in the following
scenario:
- we reference some library/SDK class, but do not acquire source-lock
This might seem a bit weird, but actually it is quite easy to achieve
as soon as we understand that analysis sources doesn't necessarily
acquires respective lock, only forcing lazy computations does so.
E.g., we can just traverse PSI tree and meet some refernce to "Any?" -
this doesn't involves acquiring source-lock
- we start resolving it, which usually involves acquiring library/SDK-lock
(e.g., in order to get it supertypes or memberScope)
- because we reference it from the source-module, we may like to refine
it, in which case we will have to acquire source-lock on refinement
cache
Obviously, that may lead to deadlocks, so, in this commit we disable
creating granular locks when we work with refinement.
Note that if refinement is disabled (which is the case for all non-MPP
projects), we still create separate locks.
This commit is contained in:
committed by
Dmitry Savvinov
parent
fd4d8176dd
commit
01f0589ddf
@@ -91,6 +91,12 @@ public class LockBasedStorageManager implements StorageManager {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " (" + debugText + ")";
|
||||
}
|
||||
|
||||
public LockBasedStorageManager replaceExceptionHandling(
|
||||
@NotNull String debugText, @NotNull ExceptionHandlingStrategy exceptionHandlingStrategy
|
||||
) {
|
||||
return new LockBasedStorageManager(debugText, exceptionHandlingStrategy, lock);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(@NotNull Function1<? super K, ? extends V> compute) {
|
||||
|
||||
+22
-12
@@ -37,12 +37,13 @@ import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSpe
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.isTypeRefinementEnabled
|
||||
import org.jetbrains.kotlin.context.GlobalContext
|
||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.project.*
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.contextWithNewLockAndCompositeExceptionTracker
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.contextWithCompositeExceptionTracker
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.outOfBlockModificationCount
|
||||
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
|
||||
@@ -70,7 +71,8 @@ data class PlatformAnalysisSettings(
|
||||
val platform: TargetPlatform, val sdk: Sdk?,
|
||||
val isAdditionalBuiltInFeaturesSupported: Boolean,
|
||||
// Effectively unused as a property. Needed only to distinguish different modes when being put in a map
|
||||
val isReleaseCoroutines: Boolean
|
||||
val isReleaseCoroutines: Boolean,
|
||||
val isTypeRefinementEnabled: Boolean
|
||||
)
|
||||
|
||||
class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
@@ -112,7 +114,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
val platform = JvmPlatforms.defaultJvmPlatform // TODO: Js scripts?
|
||||
val settings = PlatformAnalysisSettings(
|
||||
platform, sdk, true,
|
||||
LanguageFeature.ReleaseCoroutines.defaultState == LanguageFeature.State.ENABLED
|
||||
LanguageFeature.ReleaseCoroutines.defaultState == LanguageFeature.State.ENABLED,
|
||||
false // TODO: replace with correct value
|
||||
)
|
||||
|
||||
val dependenciesForScriptDependencies = listOf(
|
||||
@@ -130,7 +133,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
getOrBuildGlobalFacade(settings).facadeForSdk
|
||||
}
|
||||
|
||||
val globalContext = globalFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForScriptDependencies")
|
||||
val globalContext = globalFacade.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForScriptDependencies")
|
||||
return ProjectResolutionFacade(
|
||||
"facadeForScriptDependencies",
|
||||
resolverForScriptDependenciesName,
|
||||
@@ -160,7 +163,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
reuseDataFrom = null
|
||||
)
|
||||
|
||||
private val librariesContext = sdkContext.contextWithNewLockAndCompositeExceptionTracker(resolverForLibrariesName)
|
||||
private val librariesContext = sdkContext.contextWithCompositeExceptionTracker(settings, resolverForLibrariesName)
|
||||
val facadeForLibraries = ProjectResolutionFacade(
|
||||
"facadeForLibraries", "$resolverForLibrariesName for platform ${settings.sdk}",
|
||||
project, librariesContext, settings,
|
||||
@@ -173,7 +176,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
)
|
||||
)
|
||||
|
||||
private val modulesContext = librariesContext.contextWithNewLockAndCompositeExceptionTracker(resolverForModulesName)
|
||||
private val modulesContext = librariesContext.contextWithCompositeExceptionTracker(settings, resolverForModulesName)
|
||||
val facadeForModules = ProjectResolutionFacade(
|
||||
"facadeForModules", "$resolverForModulesName for platform ${settings.platform}",
|
||||
project, modulesContext, settings,
|
||||
@@ -190,7 +193,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = PlatformAnalysisSettings(
|
||||
targetPlatform, sdk,
|
||||
supportsAdditionalBuiltInsMembers(),
|
||||
isReleaseCoroutines()
|
||||
isReleaseCoroutines(),
|
||||
isTypeRefinementEnabled()
|
||||
)
|
||||
|
||||
private fun IdeaModuleInfo.supportsAdditionalBuiltInsMembers(): Boolean {
|
||||
@@ -205,6 +209,12 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
}
|
||||
|
||||
private fun IdeaModuleInfo.isTypeRefinementEnabled(): Boolean {
|
||||
return IDELanguageSettingsProvider
|
||||
.getLanguageVersionSettings(this, project)
|
||||
.isTypeRefinementEnabled()
|
||||
}
|
||||
|
||||
private fun globalFacade(settings: PlatformAnalysisSettings) =
|
||||
getOrBuildGlobalFacade(settings).facadeForModules
|
||||
|
||||
@@ -270,7 +280,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
specialModuleInfo is ModuleSourceInfo -> {
|
||||
val dependentModules = specialModuleInfo.getDependentModules()
|
||||
val modulesFacade = globalFacade(settings)
|
||||
val globalContext = modulesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForSpecialModuleInfo (ModuleSourceInfo)")
|
||||
val globalContext = modulesFacade.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForSpecialModuleInfo (ModuleSourceInfo)")
|
||||
makeProjectResolutionFacade(
|
||||
"facadeForSpecialModuleInfo (ModuleSourceInfo)",
|
||||
globalContext,
|
||||
@@ -283,7 +293,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
val facadeForScriptDependencies = createFacadeForScriptDependencies(
|
||||
ScriptDependenciesInfo.ForFile(project, specialModuleInfo.scriptFile, specialModuleInfo.scriptDefinition)
|
||||
)
|
||||
val globalContext = facadeForScriptDependencies.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForSpecialModuleInfo (ScriptModuleInfo)")
|
||||
val globalContext = facadeForScriptDependencies.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForSpecialModuleInfo (ScriptModuleInfo)")
|
||||
makeProjectResolutionFacade(
|
||||
"facadeForSpecialModuleInfo (ScriptModuleInfo)",
|
||||
globalContext,
|
||||
@@ -294,7 +304,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
}
|
||||
specialModuleInfo is ScriptDependenciesInfo -> facadeForScriptDependenciesForProject
|
||||
specialModuleInfo is ScriptDependenciesSourceInfo -> {
|
||||
val globalContext = facadeForScriptDependenciesForProject.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)")
|
||||
val globalContext = facadeForScriptDependenciesForProject.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)")
|
||||
makeProjectResolutionFacade(
|
||||
"facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)",
|
||||
globalContext,
|
||||
@@ -307,8 +317,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
specialModuleInfo is LibrarySourceInfo || specialModuleInfo === NotUnderContentRootModuleInfo -> {
|
||||
val librariesFacade = librariesFacade(settings)
|
||||
val debugName = "facadeForSpecialModuleInfo (LibrarySourceInfo or NotUnderContentRootModuleInfo)"
|
||||
val globalContext = librariesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker(debugName)
|
||||
|
||||
val globalContext = librariesFacade.globalContext.contextWithCompositeExceptionTracker(settings, debugName)
|
||||
|
||||
makeProjectResolutionFacade(
|
||||
debugName,
|
||||
globalContext,
|
||||
|
||||
+17
-1
@@ -6,10 +6,19 @@
|
||||
package org.jetbrains.kotlin.idea.caches.resolve.util
|
||||
|
||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.PlatformAnalysisSettings
|
||||
import org.jetbrains.kotlin.storage.ExceptionTracker
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
|
||||
internal fun GlobalContextImpl.contextWithNewLockAndCompositeExceptionTracker(debugName: String): GlobalContextImpl {
|
||||
private fun GlobalContextImpl.contextWithCompositeExceptionTracker(debugName: String): GlobalContextImpl {
|
||||
val newExceptionTracker = CompositeExceptionTracker(this.exceptionTracker)
|
||||
return GlobalContextImpl(
|
||||
storageManager.replaceExceptionHandling(debugName, newExceptionTracker),
|
||||
newExceptionTracker
|
||||
)
|
||||
}
|
||||
|
||||
private fun GlobalContextImpl.contextWithNewLockAndCompositeExceptionTracker(debugName: String): GlobalContextImpl {
|
||||
val newExceptionTracker = CompositeExceptionTracker(this.exceptionTracker)
|
||||
return GlobalContextImpl(
|
||||
LockBasedStorageManager.createWithExceptionHandling(debugName, newExceptionTracker),
|
||||
@@ -17,6 +26,13 @@ internal fun GlobalContextImpl.contextWithNewLockAndCompositeExceptionTracker(de
|
||||
)
|
||||
}
|
||||
|
||||
internal fun GlobalContextImpl.contextWithCompositeExceptionTracker(settings: PlatformAnalysisSettings, debugName: String): GlobalContextImpl =
|
||||
if (settings.isTypeRefinementEnabled) {
|
||||
this.contextWithCompositeExceptionTracker(debugName)
|
||||
} else {
|
||||
this.contextWithNewLockAndCompositeExceptionTracker(debugName)
|
||||
}
|
||||
|
||||
private class CompositeExceptionTracker(val delegate: ExceptionTracker) : ExceptionTracker() {
|
||||
override fun getModificationCount(): Long {
|
||||
return super.getModificationCount() + delegate.modificationCount
|
||||
|
||||
Reference in New Issue
Block a user