From 5632c0143e5fdedeec5516964c5d913413688f3a Mon Sep 17 00:00:00 2001 From: Alex Plate Date: Wed, 3 Jan 2024 11:15:48 +0200 Subject: [PATCH] [Workspace Model] Change the way the global model checks for recursive update The recursive check switched to the stack trace inspection. This is not the best solution, but it works reliably. For further information about the checks, please visit IDEA-342103 Report about the problem: https://youtrack.jetbrains.com/issue/IDEA-342103/Recursive-update-in-the-workspace-model#focus=Comments-27-8724580.0-0 GitOrigin-RevId: 61cb4b3fe710b966ad43f72d1fef4ba6a048a14b --- .../ide/impl/GlobalWorkspaceModel.kt | 27 +++++++++++++++---- .../ide/impl/WorkspaceModelImpl.kt | 3 +++ 2 files changed, 25 insertions(+), 5 deletions(-) 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