UnindexedFilesUpdater -> DumbModeTask; faster refresh handling

This commit is contained in:
peter
2014-04-08 15:38:22 +02:00
parent 7a17712c88
commit 14f4d6bbfb
5 changed files with 128 additions and 77 deletions
@@ -42,6 +42,7 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.FileBasedIndexProjectHandler;
import com.intellij.util.indexing.UnindexedFilesUpdater;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -307,7 +308,12 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl {
if (!myStartupActivityPerformed) return;
if (myDoLogCachesUpdate) LOG.info(new Throwable("sync roots"));
DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject);
dumbService.queueTask(new UnindexedFilesUpdater(myProject, false));
if (myRootsChangeUpdaters.isEmpty()) return;
if (ourScheduleCacheUpdateInDumbMode) {
dumbService.queueCacheUpdateInDumbMode(myRootsChangeUpdaters);
} else {
@@ -18,7 +18,6 @@ package com.intellij.util.indexing;
import com.intellij.AppTopics;
import com.intellij.history.LocalHistory;
import com.intellij.ide.caches.CacheUpdater;
import com.intellij.ide.util.DelegatingProgressIndicator;
import com.intellij.lang.ASTNode;
import com.intellij.notification.NotificationDisplayType;
@@ -229,7 +228,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
LOG.info(e);
}
}
scheduleIndexRebuild(true);
scheduleIndexRebuild();
}
});
@@ -1327,7 +1326,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
try {
doClearIndex(indexId);
if (!cleanupOnly) {
scheduleIndexRebuild(false);
scheduleIndexRebuild();
}
}
catch (StorageException e) {
@@ -1365,16 +1364,9 @@ public class FileBasedIndexImpl extends FileBasedIndex {
}
}
private void scheduleIndexRebuild(boolean forceDumbMode) {
private static void scheduleIndexRebuild() {
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
final Set<CacheUpdater> updatersToRun = Collections.<CacheUpdater>singleton(new UnindexedFilesUpdater(project, this));
final DumbServiceImpl service = DumbServiceImpl.getInstance(project);
if (forceDumbMode) {
service.queueCacheUpdateInDumbMode(updatersToRun);
}
else {
service.queueCacheUpdate(updatersToRun);
}
DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, false));
}
}
@@ -1671,6 +1663,10 @@ public class FileBasedIndexImpl extends FileBasedIndex {
return myChangedFilesCollector.getNumberOfPendingInvalidations();
}
public int getChangedFileCount() {
return myChangedFilesCollector.getAllFilesToUpdate().size();
}
@NotNull
public Collection<VirtualFile> getFilesToUpdate(final Project project) {
return ContainerUtil.findAll(myChangedFilesCollector.getAllFilesToUpdate(), new Condition<VirtualFile>() {
@@ -37,6 +37,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.util.Consumer;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -58,10 +59,18 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple
startupManager.registerPreStartupActivity(new Runnable() {
@Override
public void run() {
final UnindexedFilesUpdater unindexedFilesUpdater = new UnindexedFilesUpdater(project, index);
// dumb mode should start before post-startup activities
// only when queueTask is called from UI thread, we can guarantee that
// when the method returns, the application has entered dumb mode
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
if (!project.isDisposed()) {
DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, true));
}
}
});
startupManager.registerCacheUpdater(unindexedFilesUpdater);
rootManager.registerRootsChangeUpdater(unindexedFilesUpdater);
myIndex.registerIndexableSet(FileBasedIndexProjectHandler.this, project);
projectManager.addProjectManagerListener(project, new ProjectManagerAdapter() {
private boolean removed = false;
@@ -69,7 +78,6 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple
public void projectClosing(Project project) {
if (!removed) {
removed = true;
rootManager.unregisterRootsChangeUpdater(unindexedFilesUpdater);
myIndex.removeIndexableSet(FileBasedIndexProjectHandler.this);
}
}
@@ -111,14 +119,13 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple
@Nullable
public static DumbModeTask createChangedFilesIndexingTask(final Project project) {
final FileBasedIndexImpl index = (FileBasedIndexImpl)FileBasedIndex.getInstance();
final Collection<VirtualFile> files = index.getFilesToUpdate(project);
if (files.isEmpty()) return null;
if (files.size() + index.getNumberOfPendingInvalidations() < 20 && !DumbService.isDumb(project)) {
if (index.getChangedFileCount() + index.getNumberOfPendingInvalidations() < 20 && !DumbService.isDumb(project)) {
// the changed set is small, process it immediately without entering dumb mode
// invalidation tasks are also processed and may take some time => take them into account
try {
HeavyProcessLatch.INSTANCE.processStarted();
final Collection<VirtualFile> files = index.getFilesToUpdate(project);
reindexRefreshedFiles(new EmptyProgressIndicator(), files, project, index);
}
finally {
@@ -131,6 +138,7 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple
return new DumbModeTask() {
@Override
public void performInDumbMode(@NotNull ProgressIndicator indicator) {
final Collection<VirtualFile> files = index.getFilesToUpdate(project);
indicator.setIndeterminate(false);
indicator.setText(IdeBundle.message("progress.indexing.updating"));
reindexRefreshedFiles(indicator, files, project, index);
@@ -15,45 +15,43 @@
*/
package com.intellij.util.indexing;
import com.intellij.ide.caches.CacheUpdater;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.caches.FileContent;
import com.intellij.ide.startup.impl.StartupManagerImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.CacheUpdateRunner;
import com.intellij.openapi.project.DumbModeTask;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.CollectingContentIterator;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Eugene Zhuravlev
* @since Jan 29, 2008
*/
public class UnindexedFilesUpdater implements CacheUpdater {
public class UnindexedFilesUpdater extends DumbModeTask {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.indexing.UnindexedFilesUpdater");
private final FileBasedIndexImpl myIndex;
private final FileBasedIndexImpl myIndex = (FileBasedIndexImpl)FileBasedIndex.getInstance();
private final Project myProject;
private final AtomicBoolean myFinishedUpdate = new AtomicBoolean();
private long myStarted;
private final boolean myOnStartup;
public UnindexedFilesUpdater(final Project project, FileBasedIndexImpl index) {
myIndex = index;
public UnindexedFilesUpdater(final Project project, boolean onStartup) {
myProject = project;
myOnStartup = onStartup;
}
@Override
public int getNumberOfPendingUpdateJobs() {
return myIndex.getNumberOfPendingInvalidations();
}
private void updateUnindexedFiles(ProgressIndicator indicator) {
indicator.setIndeterminate(true);
indicator.setText(IdeBundle.message("progress.indexing.scanning"));
@NotNull
@Override
public VirtualFile[] queryNeededFiles(@NotNull ProgressIndicator indicator) {
myIndex.filesUpdateStarted(myProject);
CollectingContentIterator finder = myIndex.createContentIterator(indicator);
long l = System.currentTimeMillis();
myIndex.iterateIndexableFiles(finder, myProject, indicator);
@@ -61,35 +59,51 @@ public class UnindexedFilesUpdater implements CacheUpdater {
LOG.info("Indexable files iterated in " + (System.currentTimeMillis() - l) + " ms");
List<VirtualFile> files = finder.getFiles();
if (myOnStartup) {
// full VFS refresh makes sense only after it's loaded, i.e. after scanning files to index is finished
((StartupManagerImpl)StartupManager.getInstance(myProject)).scheduleInitialVfsRefresh();
}
if (files.isEmpty()) {
return;
}
long started = System.currentTimeMillis();
LOG.info("Unindexed files update started: " + files.size() + " files to update");
myFinishedUpdate.set(false);
myStarted = System.currentTimeMillis();
return VfsUtilCore.toVirtualFileArray(files);
indicator.setIndeterminate(false);
indicator.setText(IdeBundle.message("progress.indexing.updating"));
indexFiles(indicator, files);
LOG.info("Unindexed files update done in " + (System.currentTimeMillis() - started) + " ms");
}
private void indexFiles(ProgressIndicator indicator, List<VirtualFile> files) {
CacheUpdateRunner.processFiles(indicator, true, files, myProject, new Consumer<FileContent>() {
@Override
public void consume(FileContent content) {
try {
myIndex.indexFileContent(myProject, content);
}
finally {
IndexingStamp.flushCache(content.getVirtualFile());
}
}
});
}
@Override
public void processFile(@NotNull FileContent fileContent) {
public void performInDumbMode(@NotNull ProgressIndicator indicator) {
myIndex.filesUpdateStarted(myProject);
try {
myIndex.indexFileContent(myProject, fileContent);
updateUnindexedFiles(indicator);
}
finally {
IndexingStamp.flushCache(fileContent.getVirtualFile());
}
}
@Override
public void updatingDone() {
if (myFinishedUpdate.compareAndSet(false, true)) {
myIndex.filesUpdateFinished(myProject);
LOG.info("Unindexed files update done in " + (System.currentTimeMillis() - myStarted) + " ms");
}
}
@Override
public void canceled() {
if (myFinishedUpdate.compareAndSet(false, true)) {
myIndex.filesUpdateFinished(myProject);
catch (ProcessCanceledException e) {
LOG.info("Unindexed files update canceled");
throw e;
} finally {
myIndex.filesUpdateFinished(myProject);
}
}
}
@@ -187,27 +187,34 @@ public class StartupManagerImpl extends StartupManagerEx {
}
});
if (!app.isUnitTestMode() && !myProject.isDisposed()) {
if (!app.isHeadlessEnvironment()) {
checkProjectRoots();
final long sessionId = VirtualFileManager.getInstance().asyncRefresh(null);
final MessageBusConnection connection = app.getMessageBus().connect();
connection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() {
@Override
public void afterProjectClosed(@NotNull Project project) {
RefreshQueue.getInstance().cancelSession(sessionId);
connection.disconnect();
}
});
}
else {
VirtualFileManager.getInstance().syncRefresh();
}
}
Registry.get("ide.firstStartup").setValue(false);
}
public void scheduleInitialVfsRefresh() {
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
Application app = ApplicationManager.getApplication();
if (!app.isHeadlessEnvironment()) {
checkProjectRoots();
final long sessionId = VirtualFileManager.getInstance().asyncRefresh(null);
final MessageBusConnection connection = app.getMessageBus().connect();
connection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() {
@Override
public void afterProjectClosed(@NotNull Project project) {
RefreshQueue.getInstance().cancelSession(sessionId);
connection.disconnect();
}
});
}
else {
VirtualFileManager.getInstance().syncRefresh();
}
}
});
}
private void checkProjectRoots() {
LocalFileSystem fs = LocalFileSystem.getInstance();
if (!(fs instanceof LocalFileSystemImpl)) return;
@@ -239,7 +246,27 @@ public class StartupManagerImpl extends StartupManagerEx {
public void startCacheUpdate() {
try {
DumbServiceImpl.getInstance(myProject).queueCacheUpdateInDumbMode(myCacheUpdaters);
DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
// pre-startup activities have registered dumb tasks that load VFS (scanning files to index)
// only after these tasks pass does VFS refresh make sense
dumbService.queueTask(new DumbModeTask() {
@Override
public void performInDumbMode(@NotNull final ProgressIndicator indicator) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (!myProject.isDisposed()) {
scheduleInitialVfsRefresh();
}
}
});
}
});
}
dumbService.queueCacheUpdateInDumbMode(myCacheUpdaters);
}
catch (ProcessCanceledException e) {
throw e;