mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-247316: Fix "Pausing Indexing Applies to All Windows Rather than Current Project".
What caused the indexing to stop for all projects: 1) IndexUpdateRunner creates worker threads in a common pool of the fixed size N. These worker threads are used to index different projects one by one. 2) When indexing of some project is suspended, all worker threads are blocked (on object monitor of the "ProgressSuspender.freezeIfNeeded") on "ProgressIndicator.checkCancelled" of the corresponding project and there is no worker thread that can finish indexing of other projects. To fix this, we need to skip IndexingJob-s that have suspended progress indicator and to temporarily disable suspension when indexing a single file. GitOrigin-RevId: 5b69a41279557f14ee251d5669656a454ccaf569
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c547928559
commit
9b0cb377b4
+44
-7
@@ -5,6 +5,8 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.WrappedProgressIndicator;
|
||||
import com.intellij.openapi.progress.impl.ProgressSuspender;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
@@ -82,7 +84,9 @@ public final class IndexUpdateRunner {
|
||||
indicator.setIndeterminate(false);
|
||||
|
||||
CachedFileContentLoader contentLoader = new CurrentProjectHintedCachedFileContentLoader(project);
|
||||
IndexingJob indexingJob = new IndexingJob(project, indicator, contentLoader, files);
|
||||
ProgressIndicator originalIndicator = unwrapAll(indicator);
|
||||
ProgressSuspender originalSuspender = ProgressSuspender.getSuspender(originalIndicator);
|
||||
IndexingJob indexingJob = new IndexingJob(project, indicator, contentLoader, files, originalIndicator, originalSuspender);
|
||||
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
|
||||
// If the current thread has acquired the write lock, we can't grant it to worker threads, so we must do the work in the current thread.
|
||||
while (!indexingJob.areAllFilesProcessed()) {
|
||||
@@ -106,12 +110,13 @@ public final class IndexUpdateRunner {
|
||||
numberOfRunningWorkers.incrementAndGet();
|
||||
}
|
||||
while (!project.isDisposed() && !indexingJob.areAllFilesProcessed() && indexingJob.myError.get() == null) {
|
||||
// Add a worker if the previous died for whatever reason, to avoid waiting for nothing.
|
||||
if (numberOfRunningWorkers.get() < myNumberOfIndexingThreads) {
|
||||
// Internally checks for suspension of the indexing and blocks the current thread if necessary.
|
||||
indicator.checkCanceled();
|
||||
// Add workers if the previous have stopped for whatever reason.
|
||||
while (numberOfRunningWorkers.get() < myNumberOfIndexingThreads) {
|
||||
myIndexingExecutor.execute(worker);
|
||||
numberOfRunningWorkers.incrementAndGet();
|
||||
}
|
||||
indicator.checkCanceled();
|
||||
try {
|
||||
if (indexingJob.myAllFilesAreProcessedLatch.await(100, TimeUnit.MILLISECONDS)) {
|
||||
break;
|
||||
@@ -140,22 +145,40 @@ public final class IndexUpdateRunner {
|
||||
// Drops finished, cancelled and failed jobs from {@code ourIndexingJobs}. Does not throw exceptions.
|
||||
private void indexJobsFairly() {
|
||||
while (!ourIndexingJobs.isEmpty()) {
|
||||
boolean allJobsAreSuspended = true;
|
||||
for (IndexingJob job : ourIndexingJobs) {
|
||||
ProgressIndicator jobIndicator = job.myIndicator;
|
||||
if (job.myProject.isDisposed()
|
||||
|| job.myNoMoreFilesInQueue.get()
|
||||
|| job.myIndicator.isCanceled()
|
||||
|| jobIndicator.isCanceled()
|
||||
|| job.myError.get() != null) {
|
||||
ourIndexingJobs.remove(job);
|
||||
allJobsAreSuspended = false;
|
||||
continue;
|
||||
}
|
||||
ProgressSuspender suspender = job.myOriginalProgressSuspender;
|
||||
if (suspender != null && suspender.isSuspended()) {
|
||||
continue;
|
||||
}
|
||||
allJobsAreSuspended = false;
|
||||
try {
|
||||
indexOneFileOfJob(job);
|
||||
Runnable work = () -> indexOneFileOfJob(job);
|
||||
if (suspender != null) {
|
||||
// Here it is important to use the original progress indicator which is directly associated with the ProgressSuspender.
|
||||
suspender.executeNonSuspendableSection(job.myOriginalProgressIndicator, work);
|
||||
} else {
|
||||
work.run();
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
job.myError.compareAndSet(null, e);
|
||||
ourIndexingJobs.remove(job);
|
||||
}
|
||||
}
|
||||
if (allJobsAreSuspended) {
|
||||
// To avoid busy-looping.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,6 +378,14 @@ public final class IndexUpdateRunner {
|
||||
return file.getPath();
|
||||
}
|
||||
|
||||
private static @NotNull ProgressIndicator unwrapAll(@NotNull ProgressIndicator indicator) {
|
||||
// Can't use "ProgressWrapper.unwrapAll" here because it unwraps "ProgressWrapper"s only (not any "WrappedProgressIndicator")
|
||||
while (indicator instanceof WrappedProgressIndicator) {
|
||||
indicator = ((WrappedProgressIndicator)indicator).getOriginalProgressIndicator();
|
||||
}
|
||||
return indicator;
|
||||
}
|
||||
|
||||
private static class IndexingJob {
|
||||
final Project myProject;
|
||||
final CachedFileContentLoader myContentLoader;
|
||||
@@ -363,19 +394,25 @@ public final class IndexUpdateRunner {
|
||||
final int myTotalFiles;
|
||||
final AtomicBoolean myNoMoreFilesInQueue = new AtomicBoolean();
|
||||
final CountDownLatch myAllFilesAreProcessedLatch;
|
||||
final ProgressIndicator myOriginalProgressIndicator;
|
||||
@Nullable final ProgressSuspender myOriginalProgressSuspender;
|
||||
final IndexingJobStatistics myStatistics = new IndexingJobStatistics();
|
||||
final AtomicReference<Throwable> myError = new AtomicReference<>();
|
||||
|
||||
IndexingJob(@NotNull Project project,
|
||||
@NotNull ProgressIndicator indicator,
|
||||
@NotNull CachedFileContentLoader contentLoader,
|
||||
@NotNull Collection<VirtualFile> files) {
|
||||
@NotNull Collection<VirtualFile> files,
|
||||
@NotNull ProgressIndicator originalProgressIndicator,
|
||||
@Nullable ProgressSuspender originalProgressSuspender) {
|
||||
myProject = project;
|
||||
myIndicator = indicator;
|
||||
myTotalFiles = files.size();
|
||||
myContentLoader = contentLoader;
|
||||
myQueueOfFiles = new ArrayBlockingQueue<>(files.size(), false, files);
|
||||
myAllFilesAreProcessedLatch = new CountDownLatch(files.size());
|
||||
myOriginalProgressIndicator = originalProgressIndicator;
|
||||
myOriginalProgressSuspender = originalProgressSuspender;
|
||||
}
|
||||
|
||||
public void oneMoreFileProcessed() {
|
||||
|
||||
@@ -19,7 +19,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
@@ -37,6 +39,7 @@ public final class ProgressSuspender implements AutoCloseable {
|
||||
private volatile boolean mySuspended;
|
||||
private final CoreProgressManager.CheckCanceledHook myHook = this::freezeIfNeeded;
|
||||
private final Set<ProgressIndicator> myProgresses = ContainerUtil.newConcurrentSet();
|
||||
private final Map<ProgressIndicator, Integer> myProgressesInNonSuspendableSections = new ConcurrentHashMap<>();
|
||||
private boolean myClosed;
|
||||
|
||||
private ProgressSuspender(@NotNull ProgressIndicatorEx progress, @NotNull String suspendedText) {
|
||||
@@ -73,6 +76,15 @@ public final class ProgressSuspender implements AutoCloseable {
|
||||
return new ProgressSuspender((ProgressIndicatorEx)indicator, suspendedText);
|
||||
}
|
||||
|
||||
public void executeNonSuspendableSection(@NotNull ProgressIndicator indicator, @NotNull Runnable runnable) {
|
||||
myProgressesInNonSuspendableSections.compute(indicator, ((__, number) -> (number == null ? 0 : number) + 1));
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
myProgressesInNonSuspendableSections.compute(indicator, (__, number) -> (number == null || number <= 1 ? null : number - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ProgressSuspender getSuspender(@NotNull ProgressIndicator indicator) {
|
||||
return indicator instanceof UserDataHolder ? ((UserDataHolder)indicator).getUserData(PROGRESS_SUSPENDER) : null;
|
||||
@@ -143,6 +155,10 @@ public final class ProgressSuspender implements AutoCloseable {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (myProgressesInNonSuspendableSections.containsKey(current)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized (myLock) {
|
||||
while (mySuspended) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user