mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
make getAttributes on archive file systems non-blocking (EA-211700 freeze)
GitOrigin-RevId: 8b007c94f3c9bd0626498f2cdc24efacbb0359fa
This commit is contained in:
committed by
intellij-monorepo-bot
parent
053ff7f9b7
commit
f21bbb94a3
@@ -5,8 +5,10 @@ import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.projectRoots.JdkUtil;
|
||||
import com.intellij.openapi.util.io.FileAttributes;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.DiskQueryRelay;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
@@ -21,6 +23,7 @@ import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -30,6 +33,14 @@ public class JrtFileSystemImpl extends JrtFileSystem {
|
||||
Collections.synchronizedMap(new THashMap<>(FileUtil.PATH_HASHING_STRATEGY));
|
||||
private final AtomicBoolean mySubscribed = new AtomicBoolean(false);
|
||||
|
||||
private final DiskQueryRelay<VirtualFile, FileAttributes> myAttrGetter = new DiskQueryRelay<>(super::getAttributes);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FileAttributes getAttributes(@NotNull VirtualFile file) {
|
||||
return myAttrGetter.accessDiskWithCheckCanceled(file);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.vfs;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A utility to run a potentially long function on a pooled thread, wait for it in an interruptible way and reuse that computation if it's
|
||||
* needed again if it's still running. Function results should be ready for concurrent access, preferably thread-safe.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public class DiskQueryRelay<Param, Result> {
|
||||
private final Function<Param, Result> myFunction;
|
||||
|
||||
/**
|
||||
* We remember the submitted tasks in "myTasks" until they're finished, to avoid creating many-many similar threads
|
||||
* in case the callee is interrupted by "checkCanceled", restarted, comes again with the same query, is interrupted again, and so on.
|
||||
*/
|
||||
private final Map<Param, Future<Result>> myTasks = ContainerUtil.newConcurrentMap();
|
||||
|
||||
public DiskQueryRelay(@NotNull Function<Param, Result> function) {
|
||||
myFunction = function;
|
||||
}
|
||||
|
||||
public Result accessDiskWithCheckCanceled(@NotNull Param arg) {
|
||||
if (ProgressIndicatorProvider.getGlobalProgressIndicator() == null) {
|
||||
return myFunction.apply(arg);
|
||||
}
|
||||
|
||||
Future<Result> future = myTasks.computeIfAbsent(arg, eachArg -> ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
try {
|
||||
return myFunction.apply(eachArg);
|
||||
}
|
||||
finally {
|
||||
myTasks.remove(eachArg);
|
||||
}
|
||||
}));
|
||||
if (future.isDone()) {
|
||||
// maybe it was very fast and completed before being put into a map
|
||||
myTasks.remove(arg, future);
|
||||
}
|
||||
return ProgressIndicatorUtils.awaitWithCheckCanceled(future);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,9 +4,11 @@ package com.intellij.openapi.vfs.impl.jar;
|
||||
import com.intellij.concurrency.ConcurrentCollectionFactory;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileAttributes;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.DiskQueryRelay;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.impl.ArchiveHandler;
|
||||
@@ -32,6 +34,14 @@ public class JarFileSystemImpl extends JarFileSystem {
|
||||
myNoCopyJarDir = !runningFromDist ? null : new File(PathManager.getHomePath());
|
||||
}
|
||||
|
||||
private final DiskQueryRelay<VirtualFile, FileAttributes> myAttrGetter = new DiskQueryRelay<>(super::getAttributes);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FileAttributes getAttributes(@NotNull VirtualFile file) {
|
||||
return myAttrGetter.accessDiskWithCheckCanceled(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNoCopyJarForPath(@NotNull String pathInJar) {
|
||||
if (myNoCopyJarPaths == null) return;
|
||||
|
||||
+4
-30
@@ -4,8 +4,6 @@ package com.intellij.openapi.vfs.impl.local;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.*;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
@@ -28,8 +26,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
@@ -130,7 +126,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem {
|
||||
@NotNull
|
||||
@Override
|
||||
public String[] list(@NotNull VirtualFile file) {
|
||||
String[] names = accessDiskWithCheckCanceled(ourListTasks, convertToIOFile(file), dir -> dir.list(DirectoryAccessChecker.getFileFilter(dir)));
|
||||
String[] names = myChildrenGetter.accessDiskWithCheckCanceled(convertToIOFile(file));
|
||||
return names == null ? ArrayUtil.EMPTY_STRING_ARRAY : names;
|
||||
}
|
||||
|
||||
@@ -724,33 +720,11 @@ public abstract class LocalFileSystemBase extends LocalFileSystem {
|
||||
if (file.getParent() == null && path.startsWith("//")) {
|
||||
return FAKE_ROOT_ATTRIBUTES; // fake Windows roots
|
||||
}
|
||||
return accessDiskWithCheckCanceled(ourAttrTasks, FileUtil.toSystemDependentName(path), FileSystemUtil::getAttributes);
|
||||
return myAttrGetter.accessDiskWithCheckCanceled(FileUtil.toSystemDependentName(path));
|
||||
}
|
||||
|
||||
private static final Map<String, Future<FileAttributes>> ourAttrTasks = ContainerUtil.newConcurrentMap();
|
||||
private static final Map<File, Future<String[]>> ourListTasks = ContainerUtil.newConcurrentMap();
|
||||
|
||||
private static <T, V> V accessDiskWithCheckCanceled(Map<T, Future<V>> store, T arg, Function<T, V> fun) {
|
||||
if (ProgressIndicatorProvider.getGlobalProgressIndicator() == null) {
|
||||
return fun.apply(arg);
|
||||
}
|
||||
|
||||
// We remember the submitted tasks in "store" map until they're finished, to avoid creating many-many similar threads
|
||||
// in case the callee is interrupted by "checkCanceled", restarted, comes again with the same query, is interrupted again, and so on.
|
||||
Future<V> future = store.computeIfAbsent(arg, path -> ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
try {
|
||||
return fun.apply(path);
|
||||
}
|
||||
finally {
|
||||
store.remove(path);
|
||||
}
|
||||
}));
|
||||
if (future.isDone()) {
|
||||
// maybe it was very fast and completed before being put into a map
|
||||
store.remove(arg, future);
|
||||
}
|
||||
return ProgressIndicatorUtils.awaitWithCheckCanceled(future);
|
||||
}
|
||||
private final DiskQueryRelay<String, FileAttributes> myAttrGetter = new DiskQueryRelay<>(FileSystemUtil::getAttributes);
|
||||
private final DiskQueryRelay<File, String[]> myChildrenGetter = new DiskQueryRelay<>(dir -> dir.list(DirectoryAccessChecker.getFileFilter(dir)));
|
||||
|
||||
@Override
|
||||
public void refresh(boolean asynchronous) {
|
||||
|
||||
Reference in New Issue
Block a user