diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexTumbler.kt b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexTumbler.kt index 1b3e654b0e10..3570f8798491 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexTumbler.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexTumbler.kt @@ -11,6 +11,7 @@ import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.project.DumbModeTask import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.DumbServiceImpl import com.intellij.openapi.project.UnindexedFilesScannerExecutor import com.intellij.openapi.roots.AdditionalLibraryRootsProvider import com.intellij.openapi.util.Disposer @@ -36,8 +37,6 @@ class FileBasedIndexTumbler(private val reason: @NonNls String) { LOG.assertTrue(!app.isWriteAccessAllowed) try { if (nestedLevelCount == 0) { - val headless = app.isHeadlessEnvironment - if (!headless) { val wasUp = dumbModeSemaphore.isUp dumbModeSemaphore.down() if (wasUp) { @@ -55,7 +54,6 @@ class FileBasedIndexTumbler(private val reason: @NonNls String) { MyDumbModeTask(dumbModeSemaphore).queue(project) } } - } LOG.assertTrue(fileTypeTracker == null) fileTypeTracker = FileTypeTracker() @@ -86,18 +84,15 @@ class FileBasedIndexTumbler(private val reason: @NonNls String) { if (nestedLevelCount == 0) { try { fileBasedIndex.loadIndexes() - val headless = ApplicationManager.getApplication().isHeadlessEnvironment - if (headless) { + if (DumbServiceImpl.isSynchronousTaskExecution) { fileBasedIndex.waitUntilIndicesAreInitialized() } - if (!headless) { - for (project in ProjectUtil.getOpenProjects()) { - UnindexedFilesScannerExecutor.getInstance(project).resumeQueue(onFinish = {}) - project.getService(PerProjectIndexingQueue::class.java).resumeQueue() - FileBasedIndexInfrastructureExtension.attachAllExtensionsData(project) - } - dumbModeSemaphore.up() + for (project in ProjectUtil.getOpenProjects()) { + UnindexedFilesScannerExecutor.getInstance(project).resumeQueue(onFinish = {}) + project.getService(PerProjectIndexingQueue::class.java).resumeQueue() + FileBasedIndexInfrastructureExtension.attachAllExtensionsData(project) } + dumbModeSemaphore.up() val runRescanning = CorruptionMarker.requireInvalidation() || (Registry.`is`("run.index.rescanning.on.plugin.load.unload") || snapshot is FbiSnapshot.RebuildRequired || @@ -129,6 +124,9 @@ class FileBasedIndexTumbler(private val reason: @NonNls String) { private class MyDumbModeTask(val semaphore: Semaphore) : DumbModeTask() { override fun performInDumbMode(indicator: ProgressIndicator) { + if (DumbServiceImpl.isSynchronousTaskExecution) { + return // TODO: this will be a deadlock otherwise (IJPL-578) + } indicator.text = IndexingBundle.message("indexes.reloading") semaphore.waitFor() } 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 70c0bd8bb4d6..c0d27b7a3ac7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.kt @@ -398,7 +398,8 @@ open class DumbServiceImpl @NonInjectable @VisibleForTesting constructor(private // isRunning will be false eventually, because we are on EDT, and no new task can be queued outside the EDT // (we only wait for currently running task to terminate). myGuiDumbTaskRunner.cancelAllTasks() - while (myGuiDumbTaskRunner.isRunning.value && !myProject.isDisposed) { + mySyncDumbTaskRunner.cancelAllTasks() + while ((myGuiDumbTaskRunner.isRunning.value || mySyncDumbTaskRunner.isRunning.value) && !myProject.isDisposed) { PingProgress.interactWithEdtProgress() LockSupport.parkNanos(50000000) } @@ -576,10 +577,11 @@ open class DumbServiceImpl @NonInjectable @VisibleForTesting constructor(private @TestOnly suspend fun waitUntilFinished() { myGuiDumbTaskRunner.waitUntilFinished() + mySyncDumbTaskRunner.isRunning.first { !it } } @TestOnly - val isRunning = myGuiDumbTaskRunner.isRunning + val isRunning: Boolean = myGuiDumbTaskRunner.isRunning.value || mySyncDumbTaskRunner.isRunning.value companion object { @JvmField diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceSyncTaskQueue.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceSyncTaskQueue.java index 46ad52ac4d7e..ecb51478eef1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceSyncTaskQueue.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceSyncTaskQueue.java @@ -9,13 +9,14 @@ import com.intellij.openapi.progress.*; import com.intellij.openapi.progress.impl.CoreProgressManager; import com.intellij.openapi.project.DumbServiceMergingTaskQueue.QueuedDumbModeTask; import com.intellij.openapi.util.Disposer; +import kotlinx.coroutines.flow.MutableStateFlow; +import kotlinx.coroutines.flow.StateFlow; +import kotlinx.coroutines.flow.StateFlowKt; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.atomic.AtomicBoolean; - public final class DumbServiceSyncTaskQueue { private final Project myProject; - private final AtomicBoolean myIsRunning = new AtomicBoolean(false); + private final MutableStateFlow myIsRunning = StateFlowKt.MutableStateFlow(false); private final DumbServiceMergingTaskQueue myTaskQueue; public DumbServiceSyncTaskQueue(@NotNull Project project, @NotNull DumbServiceMergingTaskQueue queue) { @@ -58,7 +59,7 @@ public final class DumbServiceSyncTaskQueue { } } finally { - myIsRunning.set(false); + myIsRunning.setValue(false); } }; @@ -71,6 +72,10 @@ public final class DumbServiceSyncTaskQueue { } } + public void cancelAllTasks() { + myTaskQueue.cancelAllTasks(); + } + public void disposePendingTasks() { myTaskQueue.disposePendingTasks(); } @@ -100,4 +105,8 @@ public final class DumbServiceSyncTaskQueue { } }, listenerDisposable); } + + public StateFlow isRunning() { + return myIsRunning; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/MergingQueueGuiExecutor.kt b/platform/platform-impl/src/com/intellij/openapi/project/MergingQueueGuiExecutor.kt index 83eb95ab9168..a14aa9381e4a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/MergingQueueGuiExecutor.kt +++ b/platform/platform-impl/src/com/intellij/openapi/project/MergingQueueGuiExecutor.kt @@ -145,6 +145,8 @@ open class MergingQueueGuiExecutor> protected construc startedInBackground = mySingleTaskExecutor.tryStartProcess { task: AutoclosableProgressive -> try { + // TODO: there seems to be a race between mySingleTaskExecutor.tryStartProcess and FileBasedIndexTumbler. Return now + if (mySuspended.get()) return@tryStartProcess backgroundTasksSubmitted.incrementAndGet() startInBackgroundWithVisibleOrInvisibleProgress { visibleOrInvisibleIndicator -> try { diff --git a/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt b/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt index 687fa18fa87b..9c41ab6b4674 100644 --- a/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt +++ b/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt @@ -98,7 +98,7 @@ class IndexingTestUtil(private val project: Project) { } private fun isRunning(): Boolean { - return UnindexedFilesScannerExecutor.getInstance(project).isRunning.value || DumbServiceImpl.getInstance(project).isRunning.value + return UnindexedFilesScannerExecutor.getInstance(project).isRunning.value || DumbServiceImpl.getInstance(project).isRunning } suspend fun suspendUntilIndexesAreReady() {