summaryrefslogtreecommitdiff
path: root/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java')
-rw-r--r--java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java172
1 files changed, 111 insertions, 61 deletions
diff --git a/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java b/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java
index 3655942f97b7..92c6b4349189 100644
--- a/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java
+++ b/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java
@@ -16,18 +16,23 @@
package com.intellij.psi;
import com.intellij.concurrency.JobLauncher;
+import com.intellij.ide.PowerSaveMode;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationAdapter;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.application.RuntimeInterruptedException;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
+import com.intellij.openapi.progress.impl.ProgressManagerImpl;
+import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IndexNotReadyException;
@@ -37,6 +42,7 @@ import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
@@ -71,7 +77,8 @@ import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.util.*;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -87,7 +94,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
private final ApplicationEx myApplication;
private volatile boolean myDisposed;
private volatile boolean upToDate;
- private volatile boolean enabled = true;
+ private final AtomicInteger enableVetoes = new AtomicInteger(); // number of disable() calls. To enable the service, there should be at least corresponding number of enable() calls.
private final FileWriter log;
private final ProjectFileIndex myProjectFileIndex;
@@ -99,6 +106,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
ApplicationEx application,
ProjectFileIndex projectFileIndex) throws IOException {
super(project);
+ ((FutureTask)resolveProcess).run();
myApplication = application;
myProjectFileIndex = projectFileIndex;
if (ResolveScopeManagerImpl.ENABLED_REF_BACK) {
@@ -154,8 +162,8 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
return toVfString(list);
}
- private static String toVfString(@NotNull List<VirtualFile> list) {
- List<VirtualFile> sub = list.subList(0, Math.min(list.size(), 100));
+ private static String toVfString(@NotNull Collection<VirtualFile> list) {
+ List<VirtualFile> sub = new ArrayList<VirtualFile>(list).subList(0, Math.min(list.size(), 100));
return list.size() + " files: " + StringUtil.join(sub, new Function<VirtualFile, String>() {
@Override
public String fun(VirtualFile file) {
@@ -205,6 +213,17 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
enable();
}
});
+ messageBus.connect().subscribe(PowerSaveMode.TOPIC, new PowerSaveMode.Listener() {
+ @Override
+ public void powerSaveStateChanged() {
+ if (PowerSaveMode.isEnabled()) {
+ enable();
+ }
+ else {
+ disable();
+ }
+ }
+ });
myApplication.addApplicationListener(new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
@@ -232,17 +251,16 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
enable();
}
}, this);
- Disposer.register(this, HeavyProcessLatch.INSTANCE.addListener(new HeavyProcessLatch.HeavyProcessListener() {
+ HeavyProcessLatch.INSTANCE.addListener(this, new HeavyProcessLatch.HeavyProcessListener() {
@Override
public void processStarted() {
- disable();
}
@Override
public void processFinished() {
- enable();
+ wakeUp();
}
- }));
+ });
startThread();
}
@@ -368,6 +386,9 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
fileIsResolved.writeTo(new File(getStorageDirectory(), "bitSet"));
}
+ private volatile Future<?> resolveProcess = new FutureTask<Object>(EmptyRunnable.getInstance(), null); // write from EDT only
+ private volatile ProgressIndicator resolveIndicator = new EmptyProgressIndicator();
+
@Override
public void run() {
while (!myDisposed) {
@@ -375,7 +396,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
synchronized (filesToResolve) {
isEmpty = filesToResolve.isEmpty();
}
- if (!enabled || isEmpty) {
+ if (enableVetoes.get() > 0 || isEmpty || !resolveProcess.isDone() || HeavyProcessLatch.INSTANCE.isRunning()) {
try {
waitForQueue();
}
@@ -384,77 +405,70 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
}
continue;
}
+ final Set<VirtualFile> files = countFilesToResolve();
+ if (files.isEmpty()) continue;
+
upToDate = false;
- final CountDownLatch batchProcessedLatch = new CountDownLatch(1);
- ApplicationManager.getApplication().invokeLater(new Runnable() {
+
+ myApplication.invokeLater(new Runnable() {
@Override
public void run() {
- new Task.Backgroundable(myProject, "Resolving files...", true) {
+ if (!resolveProcess.isDone()) return;
+ log("Started to resolve " + files.size() + " files");
+
+ Task.Backgroundable backgroundable = new Task.Backgroundable(myProject, "Resolving files...", true) {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
- if (ApplicationManager.getApplication().isDisposed()) return;
- try {
- processBatch(indicator);
- }
- finally {
- batchProcessedLatch.countDown();
+ if (!myApplication.isDisposed()) {
+ try {
+ processBatch(indicator, files);
+ }
+ catch (RuntimeInterruptedException ignore) {
+ // see future.cancel() in disable()
+ int i = 0;
+ }
}
}
- }.queue();
+ };
+ ProgressIndicator indicator;
+ if (files.size() > 1) {
+ //show progress
+ indicator = new BackgroundableProcessIndicator(backgroundable);
+ }
+ else {
+ indicator = new MyProgress();
+ }
+ resolveIndicator = indicator;
+ resolveProcess = ProgressManagerImpl.runProcessWithProgressAsynchronously(backgroundable, indicator, null);
}
}, myProject.getDisposed());
- try {
- batchProcessedLatch.await();
- }
- catch (InterruptedException e) {
- break;
- }
-
- synchronized (filesToResolve) {
- upToDate = filesToResolve.isEmpty();
- log("upToDate = " + upToDate);
- }
flushLog();
}
}
- private void processBatch(@NotNull final ProgressIndicator indicator) {
- Set<VirtualFile> set;
- int queuedSize;
- synchronized (filesToResolve) {
- queuedSize = filesToResolve.size();
- set = new THashSet<VirtualFile>(queuedSize);
- // someone might have cleared this bit to mark file as processed
- for (VirtualFile file : filesToResolve) {
- if (fileIsInQueue.clear(getAbsId(file))) {
- set.add(file);
- }
- }
- filesToResolve.clear();
- }
+ private volatile int resolvedInPreviousBatch;
+ private void processBatch(@NotNull final ProgressIndicator indicator, @NotNull Set<VirtualFile> files) {
+ assert !myApplication.isDispatchThread();
+ final int resolvedInPreviousBatch = this.resolvedInPreviousBatch;
+ final int totalSize = files.size() + resolvedInPreviousBatch;
final ConcurrentIntObjectMap<int[]> fileToForwardIds = new StripedLockIntObjectConcurrentHashMap<int[]>();
- Set<VirtualFile> files = countAndMarkUnresolved(set, false);
- if (files.isEmpty()) return;
- final int size = files.size();
final Set<VirtualFile> toProcess = Collections.synchronizedSet(files);
- log("Started to resolve "+ size + " files (was queued "+queuedSize+")");
-
indicator.setIndeterminate(false);
ProgressIndicatorUtils.forceWriteActionPriority(indicator, (Disposable)indicator);
long start = System.currentTimeMillis();
Processor<VirtualFile> processor = new Processor<VirtualFile>() {
@Override
public boolean process(VirtualFile file) {
- double fraction = 1 - toProcess.size() * 1.0 / size;
+ double fraction = 1 - toProcess.size() * 1.0 / totalSize;
indicator.setFraction(fraction);
try {
if (file.isDirectory() || !toResolve(file, myProject)) {
return true;
}
int fileId = getAbsId(file);
- int i = size - toProcess.size();
- indicator.setText(i + "/" + size + ": Resolving " + file.getPresentableUrl());
+ int i = totalSize - toProcess.size();
+ indicator.setText(i + "/" + totalSize + ": Resolving " + file.getPresentableUrl());
int[] forwardIds = processFile(file, fileId, indicator);
if (forwardIds == null) {
//queueUpdate(file);
@@ -475,12 +489,34 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
.getInstance().invokeConcurrentlyUnderProgress(new ArrayList<VirtualFile>(files), indicator, false, false, processor);
}
finally {
+ this.resolvedInPreviousBatch = toProcess.isEmpty() ? 0 : totalSize - toProcess.size();
queue(toProcess, "re-added after fail. success=" + success);
storeIds(fileToForwardIds);
long end = System.currentTimeMillis();
- log("Resolved batch of " + (size - toProcess.size()) + " from " + size + " files in " + ((end - start) / 1000) + "sec. (Gap: " + storage.gap+")");
+ log("Resolved batch of " + (totalSize - toProcess.size()) + " from " + totalSize + " files in " + ((end - start) / 1000) + "sec. (Gap: " + storage.gap+")");
+ synchronized (filesToResolve) {
+ upToDate = filesToResolve.isEmpty();
+ log("upToDate = " + upToDate);
+ }
+ }
+ }
+
+ @NotNull
+ private Set<VirtualFile> countFilesToResolve() {
+ Set<VirtualFile> set;
+ synchronized (filesToResolve) {
+ int queuedSize = filesToResolve.size();
+ set = new THashSet<VirtualFile>(queuedSize);
+ // someone might have cleared this bit to mark file as processed
+ for (VirtualFile file : filesToResolve) {
+ if (fileIsInQueue.clear(getAbsId(file))) {
+ set.add(file);
+ }
+ }
+ filesToResolve.clear();
}
+ return countAndMarkUnresolved(set, false);
}
private static int getAbsId(@NotNull VirtualFile file) {
@@ -528,12 +564,19 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
}
private void enable() {
- enabled = true;
+ // decrement but only if it's positive
+ int vetoes;
+ do {
+ vetoes = enableVetoes.get();
+ if (vetoes == 0) break;
+ } while(!enableVetoes.compareAndSet(vetoes, vetoes-1));
wakeUp();
}
private void disable() {
- enabled = false;
+ //resolveIndicator.cancel();
+ //resolveProcess.cancel(true);
+ enableVetoes.incrementAndGet();
wakeUp();
}
@@ -591,7 +634,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
assert forwardSize == backwardSize;
// wrap in read action so that sudden quit (in write action) would not interrupt us
- ApplicationManager.getApplication().runReadAction(new Runnable() {
+ myApplication.runReadAction(new Runnable() {
@Override
public void run() {
fileToBackwardIds.forEachEntry(new TIntObjectProcedure<TIntArrayList>() {
@@ -644,7 +687,8 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
psiFile.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
- resolveReference(reference, indicator, resolved);
+ indicator.checkCanceled();
+ resolveReference(reference, resolved);
super.visitReferenceElement(reference);
}
@@ -655,7 +699,8 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
@Override
public void visitXmlElement(XmlElement element) {
for (PsiReference reference : element.getReferences()) {
- resolveReference(reference, indicator, resolved);
+ indicator.checkCanceled();
+ resolveReference(reference, resolved);
}
super.visitXmlElement(element);
}
@@ -675,8 +720,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
return forward;
}
- private void resolveReference(@NotNull PsiReference reference, @NotNull ProgressIndicator indicator, @NotNull Set<PsiElement> resolved) {
- indicator.checkCanceled();
+ private void resolveReference(@NotNull PsiReference reference, @NotNull Set<PsiElement> resolved) {
PsiElement element = reference.resolve();
if (element != null) {
resolved.add(element);
@@ -770,4 +814,10 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
}
return queued;
}
+
+ private static class MyProgress extends ProgressIndicatorBase implements Disposable{
+ @Override
+ public void dispose() {
+ }
+ }
}