IJPL-426: allow disabling "scanning.waits.for.non.dumb.mode" in tests (to help IndexingMode implement different modes)

we don't want "waiting-for-non-dumb-mode" to pause tasks submitted from ensureIndexingStatus

GitOrigin-RevId: f9ee5cac5c45353858c5444733341231bd6448bc
This commit is contained in:
Andrei.Kuznetsov
2024-05-08 19:06:32 +02:00
committed by intellij-monorepo-bot
parent 1804adbe7d
commit 711da84ad8
4 changed files with 35 additions and 9 deletions

View File

@@ -13,6 +13,7 @@ import com.intellij.openapi.progress.impl.ProgressSuspender
import com.intellij.openapi.progress.util.PingProgress
import com.intellij.openapi.project.*
import com.intellij.openapi.util.NlsContexts.ProgressText
import com.intellij.openapi.util.registry.Registry
import com.intellij.platform.util.coroutines.childScope
import com.intellij.util.gist.GistManager
import com.intellij.util.gist.GistManagerImpl
@@ -22,6 +23,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.VisibleForTesting
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.LockSupport
@@ -29,6 +31,7 @@ import java.util.concurrent.locks.LockSupport
@ApiStatus.Internal
class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: CoroutineScope) : Disposable,
UnindexedFilesScannerExecutor {
private val scanningWaitsForNonDumbModeOverride = MutableStateFlow<Boolean?>(null)
private val runningDumbTask = AtomicReference<ProgressIndicator>()
// note that shouldShowProgressIndicator = false in UnindexedFilesScannerExecutor, so there is no suspender for the progress indicator
@@ -115,6 +118,11 @@ class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: Corout
}
}
private fun scanningWaitsForNonDumbMode(override: Boolean?): Boolean = override ?: Registry.`is`("scanning.waits.for.non.dumb.mode", true)
@VisibleForTesting
fun scanningWaitsForNonDumbMode(): Boolean = scanningWaitsForNonDumbMode(scanningWaitsForNonDumbModeOverride.value)
private suspend fun waitUntilNextTaskExecutionAllowed() {
// wait until scanning is enabled
var flow: Flow<Boolean> = scanningEnabled.combine(scanningTask) { enabled, scanningTask ->
@@ -125,15 +133,25 @@ class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: Corout
// For example, PythonLanguageLevelPusher.initExtra is invoked from RequiredForSmartModeActivity and may submit additional dumb tasks.
// We want scanning to start after all these "extra" dumb tasks are finished.
// Note that a project may become dumb immediately after the check. This is not a problem - we schedule scanning anyway.
if (UnindexedFilesScannerExecutor.scanningWaitsForNonDumbMode()) {
flow = flow.combine(DumbServiceImpl.getInstance(project).isDumbAsFlow) { shouldRun, isDumb ->
shouldRun && !isDumb
if (scanningWaitsForNonDumbMode()) {
flow = flow.combine(
// nested flow is needed because of negation (!shouldWaitForNonDumb)
DumbServiceImpl.getInstance(project).isDumbAsFlow.combine(scanningWaitsForNonDumbModeOverride) { isDumb, scanningWaitsCurrentValue ->
isDumb && scanningWaitsForNonDumbMode(scanningWaitsCurrentValue)
}
) { shouldRun, shouldWaitForNonDumb ->
shouldRun && !shouldWaitForNonDumb
}
}
flow.first { it }
}
@TestOnly
fun overrideScanningWaitsForNonDumbMode(newValue: Boolean?) {
scanningWaitsForNonDumbModeOverride.value = newValue
}
private suspend fun runScanningTask(task: UnindexedFilesScanner) {
val shouldShowProgress: StateFlow<Boolean> = if (task.shouldHideProgressInSmartMode()) {
project.service<DumbModeWhileScanningTrigger>().isDumbModeForScanningActive()
@@ -277,5 +295,8 @@ class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: Corout
companion object {
private val LOG = Logger.getInstance(UnindexedFilesScannerExecutor::class.java)
@JvmStatic
fun getInstance(project: Project): UnindexedFilesScannerExecutorImpl = project.service<UnindexedFilesScannerExecutor>() as UnindexedFilesScannerExecutorImpl
}
}

View File

@@ -3,7 +3,6 @@ package com.intellij.openapi.project
import com.intellij.openapi.components.service
import com.intellij.openapi.util.NlsContexts.ProgressText
import com.intellij.openapi.util.registry.Registry
import kotlinx.collections.immutable.PersistentList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -30,9 +29,6 @@ interface UnindexedFilesScannerExecutor {
@JvmStatic
fun shouldScanInSmartMode(): Boolean = true
@JvmStatic
fun scanningWaitsForNonDumbMode(): Boolean = Registry.`is`("scanning.waits.for.non.dumb.mode", true)
fun <T: MergeableQueueTask<T>> unwrapTask(task: MergingTaskQueue.QueuedTask<T>): T {
return task.task
}

View File

@@ -13,6 +13,7 @@ import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.UnindexedFilesScannerExecutor
import com.intellij.openapi.util.Disposer
import com.intellij.platform.util.coroutines.childScope
import com.intellij.util.indexing.UnindexedFilesScannerExecutorImpl
import kotlinx.coroutines.*
import org.junit.Assert
import kotlin.time.Duration.Companion.seconds
@@ -106,11 +107,11 @@ class IndexingTestUtil(private val project: Project) {
dumbService.ensureInitialDumbTaskRequiredForSmartModeSubmitted() // TODO IJPL-578: don't submit
val scannerExecutor = UnindexedFilesScannerExecutor.getInstance(project)
val scannerExecutor = UnindexedFilesScannerExecutorImpl.getInstance(project)
// Scheduled tasks will become a running tasks soon. To avoid a race, we check scheduled tasks first
if (scannerExecutor.hasQueuedTasks) {
return if (UnindexedFilesScannerExecutor.scanningWaitsForNonDumbMode() && dumbService.isDumb) {
return if (scannerExecutor.scanningWaitsForNonDumbMode() && dumbService.isDumb) {
val isEternal = DumbModeTestUtils.isEternalDumbTaskRunning(project)
if (isEternal) {
thisLogger().debug("Do not wait for queued scanning task, because eternal dumb task is running in the project [$project]")

View File

@@ -9,6 +9,7 @@ import com.intellij.openapi.util.RecursionManager;
import com.intellij.testFramework.DumbModeTestUtils.EternalTaskShutdownToken;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.UnindexedFilesScanner;
import com.intellij.util.indexing.UnindexedFilesScannerExecutorImpl;
import junit.framework.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,6 +45,8 @@ public interface TestIndexingModeSupporter {
public @NotNull ShutdownToken setUpTestInternal(@NotNull Project project, @NotNull Disposable testRootDisposable) {
EternalTaskShutdownToken dumbTask = indexEverythingAndBecomeDumb(project);
RecursionManager.disableMissedCacheAssertions(testRootDisposable);
// we don't want "waiting-for-non-dumb-mode" to pause tasks submitted from ensureIndexingStatus
UnindexedFilesScannerExecutorImpl.getInstance(project).overrideScanningWaitsForNonDumbMode(false);
return new ShutdownToken(dumbTask);
}
@@ -57,6 +60,8 @@ public interface TestIndexingModeSupporter {
public @NotNull ShutdownToken setUpTestInternal(@NotNull Project project, @NotNull Disposable testRootDisposable) {
EternalTaskShutdownToken dumbTask = becomeDumb(project);
RecursionManager.disableMissedCacheAssertions(testRootDisposable);
// we don't want "waiting-for-non-dumb-mode" to pause tasks submitted from ensureIndexingStatus
UnindexedFilesScannerExecutorImpl.getInstance(project).overrideScanningWaitsForNonDumbMode(false);
return new ShutdownToken(dumbTask);
}
}, DUMB_EMPTY_INDEX {
@@ -97,6 +102,9 @@ public interface TestIndexingModeSupporter {
public void tearDownTest(@Nullable Project project, @NotNull ShutdownToken token) {
if (token.dumbTask != null) {
DumbModeTestUtils.endEternalDumbModeTaskAndWaitForSmartMode(project, token.dumbTask);
if (project != null) {
UnindexedFilesScannerExecutorImpl.getInstance(project).overrideScanningWaitsForNonDumbMode(null /* reset to default */);
}
}
}