experimental asynchronous event processing

This commit is contained in:
Eugene Zhuravlev
2016-10-27 13:46:31 +02:00
parent d2622e1a62
commit 4e48ad69c8
3 changed files with 85 additions and 46 deletions
@@ -27,12 +27,16 @@ import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.concurrency.SequentialTaskExecutor;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.ide.PooledThreadExecutor;
import java.io.File;
import java.util.Collection;
@@ -53,6 +57,7 @@ import java.util.Set;
*/
public class TranslatingCompilerFilesMonitor implements ApplicationComponent {
public static boolean ourDebugMode = false;
private static final SequentialTaskExecutor ourFSEventQueue = new SequentialTaskExecutor("_build_notify_queue_", PooledThreadExecutor.INSTANCE);
public TranslatingCompilerFilesMonitor(VirtualFileManager vfsManager, Application application) {
vfsManager.addVirtualFileListener(new MyVfsListener(), application);
@@ -127,74 +132,75 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent {
private static class MyVfsListener extends VirtualFileAdapter {
public void propertyChanged(@NotNull final VirtualFilePropertyEvent event) {
if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) {
final VirtualFile eventFile = event.getFile();
if (isInContentOfOpenedProject(eventFile)) {
final VirtualFile parent = event.getParent();
if (parent != null) {
final String oldName = (String)event.getOldValue();
final String root = parent.getPath() + "/" + oldName;
final Set<File> toMark = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
if (eventFile.isDirectory()) {
VfsUtilCore.visitChildrenRecursively(eventFile, new VirtualFileVisitor() {
private StringBuilder filePath = new StringBuilder(root);
@Override
public boolean visitFile(@NotNull VirtualFile child) {
if (child.isDirectory()) {
if (!Comparing.equal(child, eventFile)) {
filePath.append("/").append(child.getName());
processEventFile(event.getFile(), (eventFile)->{
if (isInContentOfOpenedProject(eventFile)) {
final VirtualFile parent = event.getParent();
if (parent != null) {
final String oldName = (String)event.getOldValue();
final String root = parent.getPath() + "/" + oldName;
final Set<File> toMark = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
if (eventFile.isDirectory()) {
VfsUtilCore.visitChildrenRecursively(eventFile, new VirtualFileVisitor() {
private StringBuilder filePath = new StringBuilder(root);
@Override
public boolean visitFile(@NotNull VirtualFile child) {
if (child.isDirectory()) {
if (!Comparing.equal(child, eventFile)) {
filePath.append("/").append(child.getName());
}
}
else {
String childPath = filePath.toString();
if (!Comparing.equal(child, eventFile)) {
childPath += "/" + child.getName();
}
toMark.add(new File(childPath));
}
return true;
}
@Override
public void afterChildrenVisited(@NotNull VirtualFile file) {
if (file.isDirectory() && !Comparing.equal(file, eventFile)) {
filePath.delete(filePath.length() - file.getName().length() - 1, filePath.length());
}
}
else {
String childPath = filePath.toString();
if (!Comparing.equal(child, eventFile)) {
childPath += "/" + child.getName();
}
toMark.add(new File(childPath));
}
return true;
}
@Override
public void afterChildrenVisited(@NotNull VirtualFile file) {
if (file.isDirectory() && !Comparing.equal(file, eventFile)) {
filePath.delete(filePath.length() - file.getName().length() - 1, filePath.length());
}
}
});
});
}
else {
toMark.add(new File(root));
}
notifyFilesDeleted(toMark);
}
else {
toMark.add(new File(root));
}
notifyFilesDeleted(toMark);
collectPathsAndNotify(eventFile, NOTIFY_CHANGED);
}
collectPathsAndNotify(eventFile, NOTIFY_CHANGED);
}
});
}
}
public void contentsChanged(@NotNull final VirtualFileEvent event) {
collectPathsAndNotify(event.getFile(), NOTIFY_CHANGED);
processEventFile(event.getFile(), (eventFile)-> collectPathsAndNotify(eventFile, NOTIFY_CHANGED));
}
public void fileCreated(@NotNull final VirtualFileEvent event) {
collectPathsAndNotify(event.getFile(), NOTIFY_CHANGED);
processEventFile(event.getFile(), (eventFile)-> collectPathsAndNotify(eventFile, NOTIFY_CHANGED));
}
public void fileCopied(@NotNull final VirtualFileCopyEvent event) {
collectPathsAndNotify(event.getFile(), NOTIFY_CHANGED);
processEventFile(event.getFile(), (eventFile)-> collectPathsAndNotify(eventFile, NOTIFY_CHANGED));
}
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
collectPathsAndNotify(event.getFile(), NOTIFY_CHANGED);
processEventFile(event.getFile(), (eventFile)-> collectPathsAndNotify(eventFile, NOTIFY_CHANGED));
}
public void beforeFileDeletion(@NotNull final VirtualFileEvent event) {
collectPathsAndNotify(event.getFile(), NOTIFY_DELETED);
processEventFile(event.getFile(), (eventFile)-> collectPathsAndNotify(eventFile, NOTIFY_DELETED));
}
public void beforeFileMovement(@NotNull final VirtualFileMoveEvent event) {
collectPathsAndNotify(event.getFile(), NOTIFY_DELETED);
processEventFile(event.getFile(), (eventFile)-> collectPathsAndNotify(eventFile, NOTIFY_DELETED));
}
}
@@ -258,4 +264,20 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent {
}
}
private static void processEventFile(final VirtualFile file, final Consumer<VirtualFile> consumer) {
if (Registry.is("build.manager.async.fs.events", false)) {
ourFSEventQueue.execute(()-> ApplicationManager.getApplication().runReadAction(()->{
if (file.isValid()) {
consumer.consume(file);
}
else {
BuildManager.getInstance().clearState();
}
}));
}
else {
consumer.consume(file);
}
}
}
@@ -106,7 +106,8 @@ import org.jetbrains.jps.incremental.Utils;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.serialization.JpsGlobalLoader;
import javax.tools.*;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.awt.*;
import java.io.File;
import java.io.IOException;
@@ -417,6 +418,20 @@ public class BuildManager implements Disposable {
scheduleAutoMake();
}
public void clearState() {
final boolean cleared;
synchronized (myProjectDataMap) {
cleared = !myProjectDataMap.isEmpty();
for (Map.Entry<String, ProjectData> entry : myProjectDataMap.entrySet()) {
cancelPreloadedBuilds(entry.getKey());
entry.getValue().dropChanges();
}
}
if (cleared) {
scheduleAutoMake();
}
}
public boolean isProjectWatched(Project project) {
return myProjectDataMap.containsKey(getProjectPath(project));
}
@@ -888,3 +888,5 @@ ide.projectView.globalOptions.description=Make Project View options such as auto
compiler.ref.index=false
compiler.ref.index.description=Enables find usages using references from compiler indices
compiler.ref.index.restartRequired=true
build.manager.async.fs.events=false
build.manager.async.fs.events.description=Experimental: TranslatingCompilerFilesMonitor will process events from VFS asynchronously to minimize write-action duration