diff --git a/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/GlobalWorkspaceModel.kt b/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/GlobalWorkspaceModel.kt index 657706db6e31..62c0a6c809ad 100644 --- a/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/GlobalWorkspaceModel.kt +++ b/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/GlobalWorkspaceModel.kt @@ -59,7 +59,8 @@ class GlobalWorkspaceModel : Disposable { var loadedFromCache = false private set - private val modelVersionUpdate = AtomicLong(-1) + private val updateModelMethodName = GlobalWorkspaceModel::updateModel.name + private val onChangedMethodName = GlobalWorkspaceModel::onChanged.name init { LOG.debug { "Loading global workspace model" } @@ -96,10 +97,7 @@ class GlobalWorkspaceModel : Disposable { @OptIn(EntityStorageInstrumentationApi::class) fun updateModel(description: @NonNls String, updater: (MutableEntityStorage) -> Unit) { ThreadingAssertions.assertWriteAccess() - if (modelVersionUpdate.get() == entityStorage.pointer.version) { - LOG.error("Trying to update global model twice from the same version. Maybe recursive call of 'updateModel'? Action:$description") - } - modelVersionUpdate.set(entityStorage.pointer.version) + checkRecursiveUpdate(description) val updateTimeMillis: Long val collectChangesTimeMillis: Long @@ -213,6 +211,25 @@ class GlobalWorkspaceModel : Disposable { filteredProject = null } + /** + * Things that must be considered if you'd love to change this logic: IDEA-342103 + */ + private fun checkRecursiveUpdate(description: @NonNls String) { + val stackStraceIterator = RuntimeException().stackTrace.iterator() + // Skip two methods of the current update + repeat(2) { stackStraceIterator.next() } + while (stackStraceIterator.hasNext()) { + val frame = stackStraceIterator.next() + if (frame.methodName == updateModelMethodName && frame.className == GlobalWorkspaceModel::class.qualifiedName) { + LOG.error("Trying to update global model twice from the same version. Maybe recursive call of 'updateModel'? Action: $description") + } + else if (frame.methodName == onChangedMethodName && frame.className == GlobalWorkspaceModel::class.qualifiedName) { + // It's fine to update the project method in "after update" listeners + return + } + } + } + private fun copyEntitiesToEmptyStorage(storage: EntityStorage, vfuManager: VirtualFileUrlManager): MutableEntityStorage { val mutableEntityStorage = MutableEntityStorage.create() // Copying global and custom libraries diff --git a/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/WorkspaceModelImpl.kt b/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/WorkspaceModelImpl.kt index 0b7abab6a6f0..6c04d07dc17d 100644 --- a/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/WorkspaceModelImpl.kt +++ b/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/impl/WorkspaceModelImpl.kt @@ -249,6 +249,9 @@ open class WorkspaceModelImpl(private val project: Project, private val cs: Coro } } + /** + * Things that must be considered if you'd love to change this logic: IDEA-342103 + */ private fun checkRecursiveUpdate() = checkRecursiveUpdateTimeMs.addMeasuredTimeMillis { val stackStraceIterator = RuntimeException().stackTrace.iterator() // Skip two methods of the current update