[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:
Denis Zharkov
2019-04-30 18:10:06 +03:00
committed by Dmitry Savvinov
parent fd4d8176dd
commit 01f0589ddf
3 changed files with 45 additions and 13 deletions
@@ -91,6 +91,12 @@ public class LockBasedStorageManager implements StorageManager {
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " (" + debugText + ")"; return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " (" + debugText + ")";
} }
public LockBasedStorageManager replaceExceptionHandling(
@NotNull String debugText, @NotNull ExceptionHandlingStrategy exceptionHandlingStrategy
) {
return new LockBasedStorageManager(debugText, exceptionHandlingStrategy, lock);
}
@NotNull @NotNull
@Override @Override
public <K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(@NotNull Function1<? super K, ? extends V> compute) { public <K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(@NotNull Function1<? super K, ? extends V> compute) {
@@ -37,12 +37,13 @@ import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSpe
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.isTypeRefinementEnabled
import org.jetbrains.kotlin.context.GlobalContext import org.jetbrains.kotlin.context.GlobalContext
import org.jetbrains.kotlin.context.GlobalContextImpl import org.jetbrains.kotlin.context.GlobalContextImpl
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.project.* import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo 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.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.caches.trackers.outOfBlockModificationCount import org.jetbrains.kotlin.idea.caches.trackers.outOfBlockModificationCount
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
@@ -70,7 +71,8 @@ data class PlatformAnalysisSettings(
val platform: TargetPlatform, val sdk: Sdk?, val platform: TargetPlatform, val sdk: Sdk?,
val isAdditionalBuiltInFeaturesSupported: Boolean, val isAdditionalBuiltInFeaturesSupported: Boolean,
// Effectively unused as a property. Needed only to distinguish different modes when being put in a map // 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 { class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
@@ -112,7 +114,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val platform = JvmPlatforms.defaultJvmPlatform // TODO: Js scripts? val platform = JvmPlatforms.defaultJvmPlatform // TODO: Js scripts?
val settings = PlatformAnalysisSettings( val settings = PlatformAnalysisSettings(
platform, sdk, true, platform, sdk, true,
LanguageFeature.ReleaseCoroutines.defaultState == LanguageFeature.State.ENABLED LanguageFeature.ReleaseCoroutines.defaultState == LanguageFeature.State.ENABLED,
false // TODO: replace with correct value
) )
val dependenciesForScriptDependencies = listOf( val dependenciesForScriptDependencies = listOf(
@@ -130,7 +133,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
getOrBuildGlobalFacade(settings).facadeForSdk getOrBuildGlobalFacade(settings).facadeForSdk
} }
val globalContext = globalFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForScriptDependencies") val globalContext = globalFacade.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForScriptDependencies")
return ProjectResolutionFacade( return ProjectResolutionFacade(
"facadeForScriptDependencies", "facadeForScriptDependencies",
resolverForScriptDependenciesName, resolverForScriptDependenciesName,
@@ -160,7 +163,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
reuseDataFrom = null reuseDataFrom = null
) )
private val librariesContext = sdkContext.contextWithNewLockAndCompositeExceptionTracker(resolverForLibrariesName) private val librariesContext = sdkContext.contextWithCompositeExceptionTracker(settings, resolverForLibrariesName)
val facadeForLibraries = ProjectResolutionFacade( val facadeForLibraries = ProjectResolutionFacade(
"facadeForLibraries", "$resolverForLibrariesName for platform ${settings.sdk}", "facadeForLibraries", "$resolverForLibrariesName for platform ${settings.sdk}",
project, librariesContext, settings, 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( val facadeForModules = ProjectResolutionFacade(
"facadeForModules", "$resolverForModulesName for platform ${settings.platform}", "facadeForModules", "$resolverForModulesName for platform ${settings.platform}",
project, modulesContext, settings, project, modulesContext, settings,
@@ -190,7 +193,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = PlatformAnalysisSettings( private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = PlatformAnalysisSettings(
targetPlatform, sdk, targetPlatform, sdk,
supportsAdditionalBuiltInsMembers(), supportsAdditionalBuiltInsMembers(),
isReleaseCoroutines() isReleaseCoroutines(),
isTypeRefinementEnabled()
) )
private fun IdeaModuleInfo.supportsAdditionalBuiltInsMembers(): Boolean { private fun IdeaModuleInfo.supportsAdditionalBuiltInsMembers(): Boolean {
@@ -205,6 +209,12 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
.supportsFeature(LanguageFeature.ReleaseCoroutines) .supportsFeature(LanguageFeature.ReleaseCoroutines)
} }
private fun IdeaModuleInfo.isTypeRefinementEnabled(): Boolean {
return IDELanguageSettingsProvider
.getLanguageVersionSettings(this, project)
.isTypeRefinementEnabled()
}
private fun globalFacade(settings: PlatformAnalysisSettings) = private fun globalFacade(settings: PlatformAnalysisSettings) =
getOrBuildGlobalFacade(settings).facadeForModules getOrBuildGlobalFacade(settings).facadeForModules
@@ -270,7 +280,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
specialModuleInfo is ModuleSourceInfo -> { specialModuleInfo is ModuleSourceInfo -> {
val dependentModules = specialModuleInfo.getDependentModules() val dependentModules = specialModuleInfo.getDependentModules()
val modulesFacade = globalFacade(settings) val modulesFacade = globalFacade(settings)
val globalContext = modulesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForSpecialModuleInfo (ModuleSourceInfo)") val globalContext = modulesFacade.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForSpecialModuleInfo (ModuleSourceInfo)")
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSpecialModuleInfo (ModuleSourceInfo)", "facadeForSpecialModuleInfo (ModuleSourceInfo)",
globalContext, globalContext,
@@ -283,7 +293,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val facadeForScriptDependencies = createFacadeForScriptDependencies( val facadeForScriptDependencies = createFacadeForScriptDependencies(
ScriptDependenciesInfo.ForFile(project, specialModuleInfo.scriptFile, specialModuleInfo.scriptDefinition) ScriptDependenciesInfo.ForFile(project, specialModuleInfo.scriptFile, specialModuleInfo.scriptDefinition)
) )
val globalContext = facadeForScriptDependencies.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForSpecialModuleInfo (ScriptModuleInfo)") val globalContext = facadeForScriptDependencies.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForSpecialModuleInfo (ScriptModuleInfo)")
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSpecialModuleInfo (ScriptModuleInfo)", "facadeForSpecialModuleInfo (ScriptModuleInfo)",
globalContext, globalContext,
@@ -294,7 +304,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
} }
specialModuleInfo is ScriptDependenciesInfo -> facadeForScriptDependenciesForProject specialModuleInfo is ScriptDependenciesInfo -> facadeForScriptDependenciesForProject
specialModuleInfo is ScriptDependenciesSourceInfo -> { specialModuleInfo is ScriptDependenciesSourceInfo -> {
val globalContext = facadeForScriptDependenciesForProject.globalContext.contextWithNewLockAndCompositeExceptionTracker("facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)") val globalContext = facadeForScriptDependenciesForProject.globalContext.contextWithCompositeExceptionTracker(settings, "facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)")
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)", "facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)",
globalContext, globalContext,
@@ -307,8 +317,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
specialModuleInfo is LibrarySourceInfo || specialModuleInfo === NotUnderContentRootModuleInfo -> { specialModuleInfo is LibrarySourceInfo || specialModuleInfo === NotUnderContentRootModuleInfo -> {
val librariesFacade = librariesFacade(settings) val librariesFacade = librariesFacade(settings)
val debugName = "facadeForSpecialModuleInfo (LibrarySourceInfo or NotUnderContentRootModuleInfo)" val debugName = "facadeForSpecialModuleInfo (LibrarySourceInfo or NotUnderContentRootModuleInfo)"
val globalContext = librariesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker(debugName) val globalContext = librariesFacade.globalContext.contextWithCompositeExceptionTracker(settings, debugName)
makeProjectResolutionFacade( makeProjectResolutionFacade(
debugName, debugName,
globalContext, globalContext,
@@ -6,10 +6,19 @@
package org.jetbrains.kotlin.idea.caches.resolve.util package org.jetbrains.kotlin.idea.caches.resolve.util
import org.jetbrains.kotlin.context.GlobalContextImpl 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.ExceptionTracker
import org.jetbrains.kotlin.storage.LockBasedStorageManager 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) val newExceptionTracker = CompositeExceptionTracker(this.exceptionTracker)
return GlobalContextImpl( return GlobalContextImpl(
LockBasedStorageManager.createWithExceptionHandling(debugName, newExceptionTracker), 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() { private class CompositeExceptionTracker(val delegate: ExceptionTracker) : ExceptionTracker() {
override fun getModificationCount(): Long { override fun getModificationCount(): Long {
return super.getModificationCount() + delegate.modificationCount return super.getModificationCount() + delegate.modificationCount