From 2bfa85dc14b38d91aceb263538453d8a012753e7 Mon Sep 17 00:00:00 2001 From: "Andrei.Kuznetsov" Date: Thu, 7 Mar 2024 16:30:15 +0100 Subject: [PATCH] IDEA-347617: let NBRA.inSmartMode, DumbService.{runWhenSmart and smartInvokeLater} wait for the same condition GitOrigin-RevId: d751da7aba8979fc455937c24079aa1563e45530 --- .../application/impl/AppUIExecutorImpl.kt | 10 +++++++++- .../intellij/openapi/project/DumbServiceImpl.kt | 16 +++++++++++++--- .../openapi/project/SmartModeScheduler.kt | 10 ++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/AppUIExecutorImpl.kt b/platform/platform-impl/src/com/intellij/openapi/application/impl/AppUIExecutorImpl.kt index c68dd3b80420..561c198d395b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/AppUIExecutorImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/AppUIExecutorImpl.kt @@ -10,7 +10,9 @@ import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.TransactionGuard import com.intellij.openapi.application.constraints.ConstrainedExecution.ContextConstraint import com.intellij.openapi.application.constraints.Expiration +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.DumbServiceImpl import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.psi.impl.PsiDocumentManagerBase @@ -174,7 +176,13 @@ internal class InSmartMode(private val project: Project) : ContextConstraint { } } - override fun isCorrectContext(): Boolean = !project.isDisposed && !DumbService.isDumb(project) + override fun isCorrectContext(): Boolean { + val correctContext = !project.isDisposed && DumbServiceImpl.getInstance(project).runWhenSmartCondition.asBoolean + if (!correctContext) { + thisLogger().debug("InSmartMode dispatched") + } + return correctContext + } override fun schedule(runnable: Runnable) { DumbService.getInstance(project).runWhenSmart(runnable) diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.kt b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.kt index 3baca2b29574..9b1f2c0ef774 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.kt @@ -47,6 +47,7 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.LockSupport +import java.util.function.BooleanSupplier import javax.swing.JComponent @ApiStatus.Internal @@ -344,6 +345,14 @@ open class DumbServiceImpl @NonInjectable @VisibleForTesting constructor(private myProject.getService(SmartModeScheduler::class.java).runWhenSmart(runnable) } + internal val runWhenSmartCondition: BooleanSupplier + get() = if (useSynchronousTaskQueue) { + BooleanSupplier { !isDumb } + } + else { + myProject.getService(SmartModeScheduler::class.java).runWhenSmartCondition + } + override fun unsafeRunWhenSmart(@Async.Schedule runnable: Runnable) { // we probably don't need unsafeRunWhenSmart anymore runWhenSmart(runnable) @@ -563,11 +572,12 @@ open class DumbServiceImpl @NonInjectable @VisibleForTesting constructor(private override fun smartInvokeLater(runnable: Runnable, modalityState: ModalityState) { ApplicationManager.getApplication().invokeLater({ - if (isDumb) { - runWhenSmart { smartInvokeLater(runnable, modalityState) } + if (runWhenSmartCondition.asBoolean) { + runnable.run() } else { - runnable.run() + LOG.debug("smartInvokeLater dispatched") + runWhenSmart { smartInvokeLater(runnable, modalityState) } } }, modalityState, myProject.disposed) } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/SmartModeScheduler.kt b/platform/platform-impl/src/com/intellij/openapi/project/SmartModeScheduler.kt index 1f7c4b0be781..58bc3ce75ba0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/SmartModeScheduler.kt +++ b/platform/platform-impl/src/com/intellij/openapi/project/SmartModeScheduler.kt @@ -25,6 +25,8 @@ import org.jetbrains.annotations.ApiStatus.Internal import org.jetbrains.annotations.Async import java.util.* import java.util.concurrent.ConcurrentLinkedDeque +import java.util.concurrent.atomic.AtomicBoolean +import java.util.function.BooleanSupplier import java.util.function.Consumer /** @@ -53,6 +55,7 @@ class SmartModeScheduler(private val project: Project, sc: CoroutineScope) : Dis private val filesScannerExecutor get() = UnindexedFilesScannerExecutor.getInstance(project) private val projectDumbState: StateFlow = dumbServiceImpl.dumbStateAsFlow private val projectScanningChanged: Flow<*> = filesScannerExecutor.startedOrStoppedEvent + internal val runWhenSmartCondition: BooleanSupplier = BooleanSupplier { getCurrentMode() == 0 } init { project.messageBus.simpleConnect().subscribe(DynamicPluginListener.TOPIC, object : DynamicPluginListener { @@ -86,7 +89,7 @@ class SmartModeScheduler(private val project: Project, sc: CoroutineScope) : Dis private fun onStateChanged() { LOG.info("State changed. Current mode: ${getCurrentMode()}") - if (isSmart()) { + if (runWhenSmartCondition.asBoolean) { // Always reschedule execution to avoid unexpected write lock acquired. // // Note2: DumbService tracks modality by itself: exit event occurs in the same modality as the enter event. @@ -96,7 +99,7 @@ class SmartModeScheduler(private val project: Project, sc: CoroutineScope) : Dis } fun runWhenSmart(runnable: Runnable) { - if (isSmart() && ApplicationManager.getApplication().isDispatchThread) { + if (runWhenSmartCondition.asBoolean && ApplicationManager.getApplication().isDispatchThread) { // Execute immediately only because some tests expect this behavior. No production need. runnable.run() } @@ -114,7 +117,7 @@ class SmartModeScheduler(private val project: Project, sc: CoroutineScope) : Dis // It may happen that one of the pending runWhenSmart actions triggers new dumb mode; // in this case we should quit processing pending actions and postpone them until the newly started dumb mode finishes. - while (isSmart()) { + while (runWhenSmartCondition.asBoolean) { val runnable = myRunWhenSmartQueue.pollFirst() ?: break resetThreadContext().use { doRun(runnable) @@ -135,7 +138,6 @@ class SmartModeScheduler(private val project: Project, sc: CoroutineScope) : Dis } } - private fun isSmart() = (getCurrentMode() == 0) fun getCurrentMode(): Int = (if (filesScannerExecutor.isRunning.value) SCANNING else 0) + (if (projectDumbState.value.isDumb) DUMB else 0)