[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 + ")";
}
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) {
@@ -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,
@@ -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