From 01f0589ddfb5bd9d273ff21c6c1fb1dcf93c751e Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 30 Apr 2019 18:10:06 +0300 Subject: [PATCH] [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. --- .../storage/LockBasedStorageManager.java | 6 ++++ .../caches/resolve/KotlinCacheServiceImpl.kt | 34 ++++++++++++------- .../caches/resolve/util/globalContextUtils.kt | 18 +++++++++- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java b/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java index d25c2f731d2..f4274c98b67 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java +++ b/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java @@ -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 MemoizedFunctionToNotNull createMemoizedFunction(@NotNull Function1 compute) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt index 7177eb2bc8e..52459e75faa 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt @@ -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, diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt index bba61cc80ae..171c0692c30 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt @@ -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