IDEA-347617: let NBRA.inSmartMode, DumbService.{runWhenSmart and smartInvokeLater} wait for the same condition

GitOrigin-RevId: d751da7aba8979fc455937c24079aa1563e45530
This commit is contained in:
Andrei.Kuznetsov
2024-03-22 00:32:45 +00:00
committed by intellij-monorepo-bot
parent a4eb2dd8ed
commit 2bfa85dc14
3 changed files with 28 additions and 8 deletions
@@ -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)
@@ -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)
}
@@ -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.DumbState> = 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)