[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
This commit is contained in:
Alex Plate
2024-01-04 12:04:57 +00:00
committed by intellij-monorepo-bot
parent 14bcae690d
commit 5632c0143e
2 changed files with 25 additions and 5 deletions
@@ -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
@@ -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