From a35b3d83a3a20f56888302f8d571836d770147df Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 13 Oct 2016 11:07:54 +0200 Subject: [PATCH] local history performance: use calculated context (workspace file, fileindex) for batch processing of virtual file events --- .../com/intellij/history/LocalHistory.java | 6 +- .../history/integration/IdeaGateway.java | 77 ++++++++++++++++--- .../LocalHistoryEventDispatcher.java | 26 ++++++- .../history/integration/LocalHistoryImpl.java | 19 ++++- 4 files changed, 115 insertions(+), 13 deletions(-) diff --git a/platform/lvcs-api/src/com/intellij/history/LocalHistory.java b/platform/lvcs-api/src/com/intellij/history/LocalHistory.java index 701a784afd31..559fdfc93c26 100644 --- a/platform/lvcs-api/src/com/intellij/history/LocalHistory.java +++ b/platform/lvcs-api/src/com/intellij/history/LocalHistory.java @@ -25,8 +25,12 @@ import org.jetbrains.annotations.Nullable; public abstract class LocalHistory { public static final Object VFS_EVENT_REQUESTOR = new Object(); + private static class LocalHistoryHolder { + static final LocalHistory ourInstance = ApplicationManager.getApplication().getComponent(LocalHistory.class); + } + public static LocalHistory getInstance() { - return ApplicationManager.getApplication().getComponent(LocalHistory.class); + return LocalHistoryHolder.ourInstance; } public abstract LocalHistoryAction startAction(@Nullable String name); diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java index fc75725cccce..7733f00e1c2a 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java @@ -40,6 +40,7 @@ import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.encoding.EncodingRegistry; import com.intellij.openapi.vfs.newvfs.ManagingFS; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; @@ -66,21 +67,79 @@ public class IdeaGateway { if (!f.isDirectory() && StringUtil.endsWith(f.getNameSequence(), ".class")) return false; - Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + LocalHistoryImpl.getInstanceImpl().dispatchPendingEvents(); + + VersionedFilterData versionedFilterData; + VfsEventDispatchContext vfsEventDispatchContext = ourCurrentEventDispatchContext.get(); + if (vfsEventDispatchContext != null) { + versionedFilterData = vfsEventDispatchContext.myFilterData; + if (versionedFilterData == null) versionedFilterData = vfsEventDispatchContext.myFilterData = new VersionedFilterData(); + } else { + versionedFilterData = new VersionedFilterData(); + } + boolean isInContent = false; - for (Project each : openProjects) { - if (each.isDefault()) continue; - if (!each.isInitialized()) continue; - if (Comparing.equal(each.getWorkspaceFile(), f)) return false; - ProjectFileIndex index = ProjectRootManager.getInstance(each).getFileIndex(); - + int numberOfOpenProjects = versionedFilterData.myOpenedProjects.size(); + for (int i = 0; i < numberOfOpenProjects; ++i) { + if (Comparing.equal(versionedFilterData.myWorkspaceFiles.get(i), f)) return false; + ProjectFileIndex index = versionedFilterData.myProjectFileIndices.get(i); + if (index.isExcluded(f)) return false; isInContent |= index.isInContent(f); } if (shouldBeInContent && !isInContent) return false; - + // optimisation: FileTypeManager.isFileIgnored(f) already checked inside ProjectFileIndex.isIgnored() - return openProjects.length != 0 || !FileTypeManager.getInstance().isFileIgnored(f); + return numberOfOpenProjects != 0 || !FileTypeManager.getInstance().isFileIgnored(f); + } + + private static final ThreadLocal ourCurrentEventDispatchContext = new ThreadLocal<>(); + + private static class VfsEventDispatchContext { + final List myEvents; + final boolean myBeforeEvents; + final VfsEventDispatchContext myPreviousContext; + + VersionedFilterData myFilterData; + + VfsEventDispatchContext(List events, boolean beforeEvents, VfsEventDispatchContext context) { + myEvents = events; + myBeforeEvents = beforeEvents; + myPreviousContext = context; + } + + public void close() { + ourCurrentEventDispatchContext.set(myPreviousContext); + } + } + + public void runWithVfsEventsDispatchContext(List events, boolean beforeEvents, Runnable action) { + VfsEventDispatchContext vfsEventDispatchContext = new VfsEventDispatchContext(events, beforeEvents, ourCurrentEventDispatchContext.get()); + ourCurrentEventDispatchContext.set(vfsEventDispatchContext); + try { + action.run(); + } finally { + vfsEventDispatchContext.close(); + } + } + + private static class VersionedFilterData { + final List myOpenedProjects = new ArrayList<>(); + final List myProjectFileIndices = new ArrayList<>(); + final List myWorkspaceFiles = new ArrayList<>(); + + VersionedFilterData() { + Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + + for (Project each : openProjects) { + if (each.isDefault()) continue; + if (!each.isInitialized()) continue; + + myWorkspaceFiles.add(each.getWorkspaceFile()); + myOpenedProjects.add(each); + myProjectFileIndices.add(ProjectRootManager.getInstance(each).getFileIndex()); + } + } } public boolean areContentChangesVersioned(@NotNull VirtualFile f) { diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java index 138d993387b2..26b22c86bfa5 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java @@ -23,10 +23,16 @@ import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter; +import com.intellij.openapi.vfs.newvfs.BulkFileListener; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements VirtualFileManagerListener, CommandListener { +import java.util.List; + +public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements VirtualFileManagerListener, CommandListener, + BulkFileListener { private static final Key WAS_VERSIONED_KEY = Key.create(LocalHistoryEventDispatcher.class.getSimpleName() + ".WAS_VERSIONED_KEY"); @@ -193,4 +199,22 @@ public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements V private boolean areContentChangesVersioned(VirtualFileEvent e) { return myGateway.areContentChangesVersioned(e.getFile()); } + + @Override + public void before(@NotNull List events) { + myGateway.runWithVfsEventsDispatchContext(events, true, () -> { + for (VFileEvent event : events) { + BulkVirtualFileListenerAdapter.fireBefore(this, event); + } + }); + } + + @Override + public void after(@NotNull List events) { + myGateway.runWithVfsEventsDispatchContext(events, false, () -> { + for (VFileEvent event : events) { + BulkVirtualFileListenerAdapter.fireAfter(this, event); + } + }); + } } \ No newline at end of file diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java index 8f1417b3dd2d..512df1f8b2c4 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryImpl.java @@ -35,6 +35,8 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.util.messages.MessageBus; +import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,6 +49,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.intellij.history.integration.LocalHistoryUtil.findRevisionIndexToRevert; public class LocalHistoryImpl extends LocalHistory implements ApplicationComponent { + private final MessageBus myBus; + private MessageBusConnection myConnection; private ChangeList myChangeList; private LocalHistoryFacade myVcs; private IdeaGateway myGateway; @@ -60,6 +64,10 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone return (LocalHistoryImpl)getInstance(); } + public LocalHistoryImpl(@NotNull MessageBus bus) { + myBus = bus; + } + @Override public void initComponent() { if (!ApplicationManager.getApplication().isUnitTestMode() && ApplicationManager.getApplication().isHeadlessEnvironment()) return; @@ -89,8 +97,10 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone CommandProcessor.getInstance().addCommandListener(myEventDispatcher); + myConnection = myBus.connect(); + myConnection.subscribe(VirtualFileManager.VFS_CHANGES, myEventDispatcher); + VirtualFileManager fm = VirtualFileManager.getInstance(); - fm.addVirtualFileListener(myEventDispatcher); fm.addVirtualFileManagerListener(myEventDispatcher); if (ApplicationManager.getApplication().isInternal() && !ApplicationManager.getApplication().isUnitTestMode()) { @@ -126,8 +136,9 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone long period = Registry.intValue("localHistory.daysToKeep") * 1000L * 60L * 60L * 24L; + myConnection.disconnect(); + myConnection = null; VirtualFileManager fm = VirtualFileManager.getInstance(); - fm.removeVirtualFileListener(myEventDispatcher); fm.removeVirtualFileManagerListener(myEventDispatcher); CommandProcessor.getInstance().removeCommandListener(myEventDispatcher); @@ -143,6 +154,10 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone ShutDownTracker.getInstance().unregisterShutdownTask(myShutdownTask); } + protected void dispatchPendingEvents() { + myConnection.deliverImmediately(); + } + @TestOnly public void cleanupForNextTest() { disposeComponent();