mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
virtual file pointers data race
This commit is contained in:
@@ -1,21 +1,27 @@
|
||||
package com.intellij.openapi.vfs;
|
||||
|
||||
import com.intellij.concurrency.JobUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.impl.VirtualFilePointerImpl;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
|
||||
import com.intellij.testFramework.IdeaTestCase;
|
||||
import com.intellij.testFramework.PlatformTestUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author dsl
|
||||
@@ -23,7 +29,13 @@ import java.util.ArrayList;
|
||||
public class VirtualFilePointerTest extends IdeaTestCase {
|
||||
private VirtualFilePointerManager myVirtualFilePointerManager;
|
||||
|
||||
static class LoggingListener implements VirtualFilePointerListener {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myVirtualFilePointerManager = VirtualFilePointerManager.getInstance();
|
||||
}
|
||||
|
||||
private static class LoggingListener implements VirtualFilePointerListener {
|
||||
private final ArrayList<String> myLog = new ArrayList<String>();
|
||||
|
||||
@Override
|
||||
@@ -100,7 +112,7 @@ public class VirtualFilePointerTest extends IdeaTestCase {
|
||||
};
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run () {
|
||||
public void run() {
|
||||
VirtualFileManager.getInstance().refresh(false);
|
||||
final VirtualFile virtualFile = getVirtualFile(tempDirectory);
|
||||
virtualFile.refresh(false, true);
|
||||
@@ -165,7 +177,7 @@ public class VirtualFilePointerTest extends IdeaTestCase {
|
||||
};
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run () {
|
||||
public void run() {
|
||||
VirtualFileManager.getInstance().refresh(false);
|
||||
final VirtualFile virtualFile = getVirtualFile(tempDirectory);
|
||||
virtualFile.refresh(false, true);
|
||||
@@ -392,9 +404,87 @@ public class VirtualFilePointerTest extends IdeaTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public void testThreads() throws IOException, InterruptedException {
|
||||
final File ioTempDir = createTempDirectory();
|
||||
final File ioPtrBase = new File(ioTempDir, "parent");
|
||||
final File ioPtr = new File(ioPtrBase, "f1");
|
||||
final File ioSand = new File(ioTempDir, "sand");
|
||||
final File ioSandPtr = new File(ioSand, "f2");
|
||||
ioSandPtr.getParentFile().mkdirs();
|
||||
ioSandPtr.createNewFile();
|
||||
ioPtr.getParentFile().mkdirs();
|
||||
ioPtr.createNewFile();
|
||||
|
||||
doVfsRefresh();
|
||||
final VirtualFilePointer pointer = createPointerByFile(ioPtr, null);
|
||||
assertTrue(pointer.isValid());
|
||||
final VirtualFile virtualFile = pointer.getFile();
|
||||
assertNotNull(virtualFile);
|
||||
assertTrue(virtualFile.isValid());
|
||||
|
||||
VirtualFileAdapter listener = new VirtualFileAdapter() {
|
||||
@Override
|
||||
public void fileCreated(VirtualFileEvent event) {
|
||||
doit(pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileDeleted(VirtualFileEvent event) {
|
||||
doit(pointer);
|
||||
}
|
||||
};
|
||||
VirtualFileManager.getInstance().addVirtualFileListener(listener, getTestRootDisposable());
|
||||
|
||||
System.out.println("------------------");
|
||||
|
||||
for (int i=0;i<1000;i++) {
|
||||
assertNotNull(pointer.getFile());
|
||||
FileUtil.delete(ioPtrBase);
|
||||
doVfsRefresh();
|
||||
|
||||
// ptr is now null, cached as map
|
||||
|
||||
final VirtualFile v = LocalFileSystem.getInstance().findFileByIoFile(ioSandPtr);
|
||||
new WriteCommandAction.Simple(getProject()) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
v.delete(this); //inc FS modCount
|
||||
LocalFileSystem.getInstance().findFileByIoFile(ioSand).createChildData(this, ioSandPtr.getName());
|
||||
}
|
||||
}.execute().throwException();
|
||||
|
||||
// ptr is still null
|
||||
|
||||
assertTrue(ioPtrBase.mkdirs());
|
||||
assertTrue(ioPtr.createNewFile());
|
||||
|
||||
doit(pointer);
|
||||
doVfsRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private static void doit(final VirtualFilePointer pointer) {
|
||||
if (((VirtualFilePointerImpl)pointer).isDisposed()) return;
|
||||
boolean b = JobUtil.invokeConcurrentlyUnderProgress(Collections.nCopies(10, null), new Processor<Object>() {
|
||||
@Override
|
||||
public boolean process(Object o) {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
public void run() {
|
||||
VirtualFile file = pointer.getFile();
|
||||
if (file != null && !file.isValid()) {
|
||||
throw new IncorrectOperationException("I've caught it. I am that good");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}, false, null);
|
||||
assertTrue(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myVirtualFilePointerManager = VirtualFilePointerManager.getInstance();
|
||||
protected boolean isRunInWriteAction() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+46
-33
@@ -18,6 +18,7 @@ package com.intellij.openapi.vfs.impl;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
@@ -57,39 +58,41 @@ public class VirtualFilePointerImpl extends UserDataHolderBase implements Virtua
|
||||
|
||||
@NotNull
|
||||
public String getFileName() {
|
||||
update();
|
||||
|
||||
if (myFile != null) {
|
||||
return myFile.getName();
|
||||
Pair<VirtualFile, String> result = update();
|
||||
VirtualFile file = result.first;
|
||||
if (file != null) {
|
||||
return file.getName();
|
||||
}
|
||||
int index = myUrl.lastIndexOf('/');
|
||||
return index >= 0 ? myUrl.substring(index + 1) : myUrl;
|
||||
String url = result.second;
|
||||
int index = url.lastIndexOf('/');
|
||||
return index >= 0 ? url.substring(index + 1) : url;
|
||||
}
|
||||
|
||||
public VirtualFile getFile() {
|
||||
checkDisposed();
|
||||
|
||||
update();
|
||||
return myFile;
|
||||
Pair<VirtualFile, String> result = update();
|
||||
return result.first;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getUrl() {
|
||||
//checkDisposed(); no check here since Disposer might want to compute hashcode during dispose()
|
||||
update();
|
||||
return getUrlNoUpdate();
|
||||
|
||||
Pair<VirtualFile, String> result = update();
|
||||
return getUrlNoUpdate(result.first, result.second);
|
||||
}
|
||||
|
||||
private String getUrlNoUpdate() {
|
||||
return myUrl == null ? myFile.getUrl() : myUrl;
|
||||
private static String getUrlNoUpdate(VirtualFile file, String url) {
|
||||
return url == null ? file.getUrl() : url;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getPresentableUrl() {
|
||||
checkDisposed();
|
||||
update();
|
||||
Pair<VirtualFile, String> result = update();
|
||||
|
||||
return PathUtil.toPresentableUrl(getUrl());
|
||||
return PathUtil.toPresentableUrl(result.second);
|
||||
}
|
||||
|
||||
private void checkDisposed() {
|
||||
@@ -138,36 +141,42 @@ public class VirtualFilePointerImpl extends UserDataHolderBase implements Virtua
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
update();
|
||||
return !disposed && myFile != null;
|
||||
Pair<VirtualFile, String> result = update();
|
||||
return !disposed && result.first != null;
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (disposed) return;
|
||||
Pair<VirtualFile, String> update() {
|
||||
if (disposed) return null;
|
||||
long lastUpdated = myLastUpdated;
|
||||
String url = myUrl;
|
||||
VirtualFile file = myFile;
|
||||
long fsModCount = myVirtualFileManager.getModificationCount();
|
||||
if (myLastUpdated == fsModCount) return;
|
||||
myLastUpdated = fsModCount;
|
||||
if (lastUpdated == fsModCount) return Pair.create(file, url);
|
||||
|
||||
if (myFile != null && !myFile.isValid()) {
|
||||
myUrl = myFile.getUrl();
|
||||
myFile = null;
|
||||
if (file != null && !file.isValid()) {
|
||||
url = file.getUrl();
|
||||
file = null;
|
||||
}
|
||||
if (myFile == null) {
|
||||
LOG.assertTrue(myUrl != null, "Both file & url are null");
|
||||
myFile = myVirtualFileManager.findFileByUrl(myUrl);
|
||||
if (myFile != null) {
|
||||
myUrl = null;
|
||||
if (file == null) {
|
||||
LOG.assertTrue(url != null, "Both file & url are null");
|
||||
file = myVirtualFileManager.findFileByUrl(url);
|
||||
if (file != null) {
|
||||
url = null;
|
||||
}
|
||||
}
|
||||
else if (!myFile.exists()) {
|
||||
myUrl = myFile.getUrl();
|
||||
myFile = null;
|
||||
if (file != null && !file.exists()) {
|
||||
url = file.getUrl();
|
||||
file = null;
|
||||
}
|
||||
myFile = file;
|
||||
myUrl = url;
|
||||
myLastUpdated = fsModCount;
|
||||
return Pair.create(file, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getUrlNoUpdate();
|
||||
return getUrlNoUpdate(myFile, myUrl);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
@@ -178,9 +187,13 @@ public class VirtualFilePointerImpl extends UserDataHolderBase implements Virtua
|
||||
if (TRACE_CREATION) {
|
||||
putUserData(KILL_TRACE, new Throwable());
|
||||
}
|
||||
String url = getUrlNoUpdate();
|
||||
String url = getUrlNoUpdate(myFile, myUrl);
|
||||
disposed = true;
|
||||
((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).clearPointerCaches(url, myListener);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -332,9 +332,6 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
}
|
||||
}
|
||||
|
||||
//if (myListenerToPointersMap.isEmpty()) {
|
||||
// System.err.println("All pointers are disposed");
|
||||
//}
|
||||
synchronized (myContainers) {
|
||||
if (!myContainers.isEmpty()) {
|
||||
VirtualFilePointerContainerImpl container = myContainers.iterator().next();
|
||||
|
||||
+44
-27
@@ -20,11 +20,13 @@
|
||||
package com.intellij.openapi.vfs.newvfs.impl;
|
||||
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem;
|
||||
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
|
||||
@@ -41,9 +43,11 @@ import java.util.*;
|
||||
|
||||
public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
private final NewVirtualFileSystem myFS;
|
||||
|
||||
// guarded by this
|
||||
private Object myChildren; // Either HashMap<String, VFile> or VFile[]
|
||||
|
||||
public VirtualDirectoryImpl(final String name, final VirtualDirectoryImpl parent, final NewVirtualFileSystem fs, final int id) {
|
||||
public VirtualDirectoryImpl(@NotNull String name, final VirtualDirectoryImpl parent, @NotNull NewVirtualFileSystem fs, final int id) {
|
||||
super(name, parent, id);
|
||||
myFS = fs;
|
||||
}
|
||||
@@ -54,24 +58,34 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NewVirtualFile findChild(final String name, final boolean createIfNotFound, boolean ensureCanonicalName) {
|
||||
final NewVirtualFile result = doFindChild(name, createIfNotFound, ensureCanonicalName);
|
||||
synchronized (this) {
|
||||
if (result == null && myChildren instanceof Map) {
|
||||
ensureAsMap().put(name, NullVirtualFile.INSTANCE);
|
||||
private NewVirtualFile findChild(@NotNull String name, final boolean createIfNotFound, boolean ensureCanonicalName) {
|
||||
final VirtualFile result = doFindChild(name, createIfNotFound, ensureCanonicalName);
|
||||
if (result == NullVirtualFile.INSTANCE) {
|
||||
return createIfNotFound ? createAndFindChildWithEventFire(name) : null;
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
synchronized (this) {
|
||||
Map<String, VirtualFile> map = asMap();
|
||||
if (map != null) {
|
||||
map.put(name, NullVirtualFile.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return (NewVirtualFile)result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NewVirtualFile doFindChild(String name, final boolean createIfNotFound, boolean ensureCanonicalName) {
|
||||
private VirtualFile doFindChild(@NotNull String name, final boolean createIfNotFound, boolean ensureCanonicalName) {
|
||||
if (name.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final VirtualFile[] a = asArray();
|
||||
final VirtualFile[] a;
|
||||
synchronized (this) {
|
||||
a = asArray();
|
||||
}
|
||||
if (a != null) {
|
||||
Object encoded = encodeName(name);
|
||||
byte[] bytes = encoded instanceof byte[] ? (byte[])encoded : null;
|
||||
@@ -89,11 +103,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
file = map.get(name);
|
||||
}
|
||||
|
||||
if (file == NullVirtualFile.INSTANCE) {
|
||||
return createIfNotFound ? createAndFindChildWithEventFire(name) : null;
|
||||
}
|
||||
|
||||
if (file != null) return (NewVirtualFile)file;
|
||||
if (file != null) return file;
|
||||
|
||||
if (ensureCanonicalName) {
|
||||
final NewVirtualFileSystem delegate = getFileSystem();
|
||||
@@ -131,6 +141,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
return name.equalsIgnoreCase(name2);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VirtualFileSystemEntry createChild(String name, int id) {
|
||||
final VirtualFileSystemEntry child;
|
||||
final NewVirtualFileSystem fs = getFileSystem();
|
||||
@@ -149,7 +160,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NewVirtualFile createAndFindChildWithEventFire(final String name) {
|
||||
private NewVirtualFile createAndFindChildWithEventFire(@NotNull String name) {
|
||||
final NewVirtualFileSystem delegate = getFileSystem();
|
||||
VirtualFile fake = new FakeVirtualFile(this, name);
|
||||
if (delegate.exists(fake)) {
|
||||
@@ -164,12 +175,12 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public NewVirtualFile refreshAndFindChild(final String name) {
|
||||
public NewVirtualFile refreshAndFindChild(@NotNull String name) {
|
||||
return findChild(name, true, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized NewVirtualFile findChildIfCached(final String name) {
|
||||
public synchronized NewVirtualFile findChildIfCached(@NotNull String name) {
|
||||
final VirtualFile[] a = asArray();
|
||||
if (a != null) {
|
||||
Object encoded = encodeName(name);
|
||||
@@ -191,6 +202,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Iterable<VirtualFile> iterInDbChildren() {
|
||||
return ContainerUtil.iterate(getInDbChildren(), new Condition<VirtualFile>() {
|
||||
public boolean value(VirtualFile file) {
|
||||
@@ -228,17 +240,19 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
return (VirtualFile[])myChildren;
|
||||
}
|
||||
|
||||
final int[] childrenIds = ourPersistence.listIds(this);
|
||||
Pair<String[],int[]> pair = PersistentFS.listAll(this);
|
||||
final int[] childrenIds = pair.second;
|
||||
VirtualFile[] children;
|
||||
if (childrenIds.length == 0) {
|
||||
children = EMPTY_ARRAY;
|
||||
}
|
||||
else {
|
||||
children = new VirtualFile[childrenIds.length];
|
||||
String[] names = pair.first;
|
||||
final Map<String, VirtualFile> map = asMap();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
final int childId = childrenIds[i];
|
||||
final String name = ourPersistence.getName(childId);
|
||||
final String name = names[i];
|
||||
VirtualFile child = map != null ? map.get(name) : null;
|
||||
|
||||
children[i] = child != null && child != NullVirtualFile.INSTANCE ? child : createChild(name, childId);
|
||||
@@ -293,21 +307,21 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized VirtualFile[] asArray() {
|
||||
private VirtualFile[] asArray() {
|
||||
if (myChildren instanceof VirtualFile[]) return (VirtualFile[])myChildren;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private synchronized Map<String, VirtualFile> asMap() {
|
||||
private Map<String, VirtualFile> asMap() {
|
||||
if (myChildren instanceof Map) return (Map<String, VirtualFile>)myChildren;
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private synchronized Map<String, VirtualFile> ensureAsMap() {
|
||||
private Map<String, VirtualFile> ensureAsMap() {
|
||||
Map<String, VirtualFile> map;
|
||||
if (myChildren == null) {
|
||||
map = createMap();
|
||||
@@ -320,7 +334,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
return map;
|
||||
}
|
||||
|
||||
public synchronized void addChild(VirtualFile file) {
|
||||
public synchronized void addChild(@NotNull VirtualFile file) {
|
||||
final VirtualFile[] a = asArray();
|
||||
if (a != null) {
|
||||
myChildren = ArrayUtil.append(a, file);
|
||||
@@ -331,7 +345,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeChild(VirtualFile file) {
|
||||
public synchronized void removeChild(@NotNull VirtualFile file) {
|
||||
final VirtualFile[] a = asArray();
|
||||
if (a != null) {
|
||||
myChildren = ArrayUtil.remove(a, file);
|
||||
@@ -346,7 +360,8 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
return myChildren instanceof VirtualFile[];
|
||||
}
|
||||
|
||||
public synchronized List<String> getSuspicousNames() {
|
||||
@NotNull
|
||||
public synchronized List<String> getSuspiciousNames() {
|
||||
final Map<String, VirtualFile> map = asMap();
|
||||
if (map == null) return Collections.emptyList();
|
||||
|
||||
@@ -379,6 +394,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, VirtualFile> createMap() {
|
||||
return getFileSystem().isCaseSensitive()
|
||||
? new THashMap<String, VirtualFile>()
|
||||
@@ -386,14 +402,15 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public synchronized void cleanupCachedChildren(Set<VirtualFile> survivors) {
|
||||
public synchronized void cleanupCachedChildren(@NotNull Set<VirtualFile> survivors) {
|
||||
if (survivors.contains(this)) {
|
||||
for (VirtualFile file : getCachedChildren()) {
|
||||
if (file instanceof VirtualDirectoryImpl) {
|
||||
((VirtualDirectoryImpl)file).cleanupCachedChildren(survivors);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
myChildren = null;
|
||||
}
|
||||
}
|
||||
|
||||
+89
-83
@@ -24,6 +24,7 @@ import com.intellij.openapi.Forceable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
|
||||
import com.intellij.openapi.util.io.ByteSequence;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -88,7 +89,7 @@ public class FSRecords implements Forceable {
|
||||
private static final String CHILDREN_ATT = "FsRecords.DIRECTORY_CHILDREN";
|
||||
private static final Object lock = new Object();
|
||||
|
||||
private volatile static int ourLocalModificationCount = 0;
|
||||
private static volatile int ourLocalModificationCount = 0;
|
||||
|
||||
private static final int FREE_RECORD_FLAG = 0x100;
|
||||
private static final int ALL_VALID_FLAGS = PersistentFS.ALL_VALID_FLAGS | FREE_RECORD_FLAG;
|
||||
@@ -205,8 +206,8 @@ public class FSRecords implements Forceable {
|
||||
boolean deleted = true;
|
||||
deleted &= FileUtil.delete(getCorruptionMarkerFile());
|
||||
deleted &= deleteWithSubordinates(namesFile);
|
||||
deleted &= Storage.deleteFiles(attributesFile.getCanonicalPath());
|
||||
deleted &= Storage.deleteFiles(contentsFile.getCanonicalPath());
|
||||
deleted &= AbstractStorage.deleteFiles(attributesFile.getCanonicalPath());
|
||||
deleted &= AbstractStorage.deleteFiles(contentsFile.getCanonicalPath());
|
||||
deleted &= deleteWithSubordinates(recordsFile);
|
||||
|
||||
if (!deleted) {
|
||||
@@ -259,7 +260,12 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
private static File basePath() {
|
||||
return new File(PathManager.getSystemPath() + "/caches/");
|
||||
return new File(getCachesDir());
|
||||
}
|
||||
|
||||
private static String getCachesDir() {
|
||||
String dir = System.getProperty("caches_dir");
|
||||
return dir == null ? PathManager.getSystemPath() + "/caches/" : dir;
|
||||
}
|
||||
|
||||
private static boolean deleteWithSubordinates(File file) {
|
||||
@@ -280,7 +286,7 @@ public class FSRecords implements Forceable {
|
||||
return ok;
|
||||
}
|
||||
|
||||
private static void markDirty() throws IOException {
|
||||
private static void markDirty() {
|
||||
if (!myDirty) {
|
||||
myDirty = true;
|
||||
myRecords.putInt(HEADER_CONNECTION_STATUS_OFFSET, CONNECTED_MAGIC);
|
||||
@@ -302,13 +308,8 @@ public class FSRecords implements Forceable {
|
||||
|
||||
public static void force() {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
if (myRecords != null) {
|
||||
markClean();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
if (myRecords != null) {
|
||||
markClean();
|
||||
}
|
||||
if (myNames != null) {
|
||||
myNames.force();
|
||||
@@ -329,12 +330,7 @@ public class FSRecords implements Forceable {
|
||||
final boolean attribsFlushed = myAttributes.flushSome();
|
||||
final boolean contentsFlushed = myContents.flushSome();
|
||||
if (attribsFlushed && contentsFlushed) {
|
||||
try {
|
||||
markClean();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
markClean();
|
||||
myRecords.force();
|
||||
}
|
||||
}
|
||||
@@ -345,7 +341,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
|
||||
private static int getVersion() throws IOException {
|
||||
private static int getVersion() {
|
||||
final int recordsVersion = myRecords.getInt(HEADER_VERSION_OFFSET);
|
||||
if (myAttributes.getVersion() != recordsVersion || myContents.getVersion() != recordsVersion) return -1;
|
||||
|
||||
@@ -356,7 +352,7 @@ public class FSRecords implements Forceable {
|
||||
return myRecords.getLong(HEADER_TIMESTAMP_OFFSET);
|
||||
}
|
||||
|
||||
private static void setCurrentVersion() throws IOException {
|
||||
private static void setCurrentVersion() {
|
||||
myRecords.putInt(HEADER_VERSION_OFFSET, VERSION);
|
||||
myRecords.putLong(HEADER_TIMESTAMP_OFFSET, System.currentTimeMillis());
|
||||
myAttributes.setVersion(VERSION);
|
||||
@@ -364,7 +360,7 @@ public class FSRecords implements Forceable {
|
||||
myRecords.putInt(HEADER_CONNECTION_STATUS_OFFSET, SAFELY_CLOSED_MAGIC);
|
||||
}
|
||||
|
||||
public static void cleanRecord(final int id) throws IOException {
|
||||
public static void cleanRecord(final int id) {
|
||||
myRecords.put(id * RECORD_SIZE, ZEROES, 0, RECORD_SIZE);
|
||||
}
|
||||
|
||||
@@ -376,7 +372,7 @@ public class FSRecords implements Forceable {
|
||||
return myRecords;
|
||||
}
|
||||
|
||||
public void dispose() throws IOException {
|
||||
public static void dispose() throws IOException {
|
||||
synchronized (lock) {
|
||||
refCount--;
|
||||
if (refCount == 0) {
|
||||
@@ -413,7 +409,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private static void markClean() throws IOException {
|
||||
private static void markClean() {
|
||||
if (myDirty) {
|
||||
myDirty = false;
|
||||
myRecords.putInt(HEADER_CONNECTION_STATUS_OFFSET, myCorrupted ? CORRUPTED_MAGIC : SAFELY_CLOSED_MAGIC);
|
||||
@@ -449,11 +445,11 @@ public class FSRecords implements Forceable {
|
||||
public FSRecords() {
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
public static void connect() {
|
||||
DbConnection.connect();
|
||||
}
|
||||
|
||||
public long getCreationTimestamp() {
|
||||
public static long getCreationTimestamp() {
|
||||
synchronized (lock) {
|
||||
return DbConnection.getTimestamp();
|
||||
}
|
||||
@@ -500,7 +496,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRecordRecursively(int id) {
|
||||
public static void deleteRecordRecursively(int id) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.markDirty();
|
||||
@@ -513,7 +509,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private void doDeleteRecursively(final int id) {
|
||||
private static void doDeleteRecursively(final int id) {
|
||||
for (int subrecord : list(id)) {
|
||||
doDeleteRecursively(subrecord);
|
||||
}
|
||||
@@ -521,7 +517,7 @@ public class FSRecords implements Forceable {
|
||||
deleteRecord(id);
|
||||
}
|
||||
|
||||
private void deleteRecord(final int id) {
|
||||
private static void deleteRecord(final int id) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.markDirty();
|
||||
@@ -536,7 +532,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteContentAndAttributes(int id) throws IOException {
|
||||
private static void deleteContentAndAttributes(int id) throws IOException {
|
||||
int content_page = getContentRecordId(id);
|
||||
if (content_page != 0) {
|
||||
getContentStorage().releaseRecord(content_page);
|
||||
@@ -555,12 +551,12 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private void addToFreeRecordsList(int id) throws IOException {
|
||||
private static void addToFreeRecordsList(int id) {
|
||||
DbConnection.addFreeRecord(id);
|
||||
setFlags(id, FREE_RECORD_FLAG, false);
|
||||
}
|
||||
|
||||
public int[] listRoots() throws IOException {
|
||||
public static int[] listRoots() throws IOException {
|
||||
synchronized (lock) {
|
||||
DbConnection.markDirty();
|
||||
final DataInputStream input = readAttribute(1, CHILDREN_ATT);
|
||||
@@ -591,7 +587,7 @@ public class FSRecords implements Forceable {
|
||||
return DbConnection.isDirty();
|
||||
}
|
||||
|
||||
public int findRootRecord(String rootUrl) throws IOException {
|
||||
public static int findRootRecord(String rootUrl) throws IOException {
|
||||
synchronized (lock) {
|
||||
DbConnection.markDirty();
|
||||
final int root = getNames().enumerate(rootUrl);
|
||||
@@ -641,7 +637,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRootRecord(int id) throws IOException {
|
||||
public static void deleteRootRecord(int id) throws IOException {
|
||||
synchronized (lock) {
|
||||
DbConnection.markDirty();
|
||||
final DataInputStream input = readAttribute(1, CHILDREN_ATT);
|
||||
@@ -683,7 +679,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public int[] list(int id) {
|
||||
public static int[] list(int id) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
final DataInputStream input = readAttribute(id, CHILDREN_ATT);
|
||||
@@ -702,8 +698,30 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
}
|
||||
public static Pair<String[],int[]> listAll(int parentId) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
final DataInputStream input = readAttribute(parentId, CHILDREN_ATT);
|
||||
if (input == null) return Pair.create(ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_INT_ARRAY);
|
||||
|
||||
public boolean wereChildrenAccessed(int id) {
|
||||
final int count = input.readInt();
|
||||
final int[] ids = ArrayUtil.newIntArray(count);
|
||||
final String[] names = ArrayUtil.newStringArray(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
int id = input.readInt();
|
||||
ids[i] = id;
|
||||
names[i] = getName(id);
|
||||
}
|
||||
input.close();
|
||||
return Pair.create(names, ids);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw DbConnection.handleError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean wereChildrenAccessed(int id) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
return findAttributePage(id, CHILDREN_ATT, false) != 0;
|
||||
@@ -714,7 +732,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateList(int id, int[] children) {
|
||||
public static void updateList(int id, int[] children) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.markDirty();
|
||||
@@ -736,7 +754,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private static void incModCount(int id) throws IOException {
|
||||
private static void incModCount(int id) {
|
||||
ourLocalModificationCount++;
|
||||
final int count = getModCount() + 1;
|
||||
getRecords().putInt(HEADER_GLOBAL_MODCOUNT_OFFSET, count);
|
||||
@@ -775,7 +793,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setParent(int id, int parent) {
|
||||
public static void setParent(int id, int parent) {
|
||||
if (id == parent) {
|
||||
LOG.error("Cyclic parent/child relations");
|
||||
return;
|
||||
@@ -805,7 +823,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(int id, String name) {
|
||||
public static void setName(int id, String name) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.markDirty();
|
||||
@@ -824,7 +842,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setFlags(int id, int flags, final boolean markAsChange) {
|
||||
public static void setFlags(int id, int flags, final boolean markAsChange) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
if (markAsChange) {
|
||||
@@ -845,7 +863,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setLength(int id, long len) {
|
||||
public static void setLength(int id, long len) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.markDirty();
|
||||
@@ -864,7 +882,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setTimestamp(int id, long value) {
|
||||
public static void setTimestamp(int id, long value) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.markDirty();
|
||||
@@ -883,23 +901,23 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setModCount(int id, int value) throws IOException {
|
||||
private static void setModCount(int id, int value) {
|
||||
putRecordInt(id, MODCOUNT_OFFSET, value);
|
||||
}
|
||||
|
||||
private static int getContentRecordId(int fileId) throws IOException {
|
||||
private static int getContentRecordId(int fileId) {
|
||||
return getRecordInt(fileId, CONTENT_OFFSET);
|
||||
}
|
||||
|
||||
private static void setContentRecordId(int id, int value) throws IOException {
|
||||
private static void setContentRecordId(int id, int value) {
|
||||
putRecordInt(id, CONTENT_OFFSET, value);
|
||||
}
|
||||
|
||||
private static int getAttributeRecordId(int id) throws IOException {
|
||||
private static int getAttributeRecordId(int id) {
|
||||
return getRecordInt(id, ATTREF_OFFSET);
|
||||
}
|
||||
|
||||
private static void setAttributeRecordId(int id, int value) throws IOException {
|
||||
private static void setAttributeRecordId(int id, int value) {
|
||||
putRecordInt(id, ATTREF_OFFSET, value);
|
||||
}
|
||||
|
||||
@@ -916,7 +934,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataInputStream readContent(int fileId) {
|
||||
public static DataInputStream readContent(int fileId) {
|
||||
try {
|
||||
int page;
|
||||
synchronized (lock) {
|
||||
@@ -931,7 +949,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataInputStream readContentById(int contentId) {
|
||||
public static DataInputStream readContentById(int contentId) {
|
||||
try {
|
||||
return getContentStorage().readStream(contentId);
|
||||
}
|
||||
@@ -941,7 +959,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataInputStream readAttribute(int fileId, String attId) {
|
||||
public static DataInputStream readAttribute(int fileId, String attId) {
|
||||
try {
|
||||
synchronized (attId) {
|
||||
int page;
|
||||
@@ -957,7 +975,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private int findContentPage(int fileId, boolean toWrite) throws IOException {
|
||||
private static int findContentPage(int fileId, boolean toWrite) throws IOException {
|
||||
checkFileIsValid(fileId);
|
||||
|
||||
int recordId = getContentRecordId(fileId);
|
||||
@@ -971,7 +989,7 @@ public class FSRecords implements Forceable {
|
||||
return recordId;
|
||||
}
|
||||
|
||||
private int findAttributePage(int fileId, String attrId, boolean toWrite) throws IOException {
|
||||
private static int findAttributePage(int fileId, String attrId, boolean toWrite) throws IOException {
|
||||
checkFileIsValid(fileId);
|
||||
|
||||
Storage storage = getAttributesStorage();
|
||||
@@ -1012,13 +1030,13 @@ public class FSRecords implements Forceable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void checkFileIsValid(int fileId) {
|
||||
private static void checkFileIsValid(int fileId) {
|
||||
assert fileId > 0 : "assert fileId > 0 failed";
|
||||
// TODO: This assertion is a bit timey, will remove when bug is caught.
|
||||
assert (getFlags(fileId) & FREE_RECORD_FLAG) == 0 : "Trying to find an attribute of deleted page";
|
||||
}
|
||||
|
||||
public int acquireFileContent(int fileId) {
|
||||
public static int acquireFileContent(int fileId) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
int record = getContentRecordId(fileId);
|
||||
@@ -1031,7 +1049,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseContent(int contentId) {
|
||||
public static void releaseContent(int contentId) {
|
||||
try {
|
||||
getContentStorage().releaseRecord(contentId);
|
||||
}
|
||||
@@ -1040,7 +1058,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
public int getContentId(int fileId) {
|
||||
public static int getContentId(int fileId) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
return getContentRecordId(fileId);
|
||||
@@ -1052,15 +1070,15 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataOutputStream writeContent(int fileId, boolean readOnly) {
|
||||
public static DataOutputStream writeContent(int fileId, boolean readOnly) {
|
||||
return new ContentOutputStream(fileId, readOnly);
|
||||
}
|
||||
|
||||
public void writeContent(int fileId, ByteSequence bytes, boolean readOnly) throws IOException {
|
||||
public static void writeContent(int fileId, ByteSequence bytes, boolean readOnly) throws IOException {
|
||||
new ContentOutputStream(fileId, readOnly).writeBytes(bytes, fileId);
|
||||
}
|
||||
|
||||
public int storeUnlinkedContent(byte[] bytes) {
|
||||
public static int storeUnlinkedContent(byte[] bytes) {
|
||||
try {
|
||||
int recordId = getContentStorage().acquireNewRecord();
|
||||
AbstractStorage.StorageDataOutput output = getContentStorage().writeStream(recordId);
|
||||
@@ -1074,12 +1092,11 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataOutputStream writeAttribute(final int fileId, final String attId, boolean fixedSize) {
|
||||
public static DataOutputStream writeAttribute(final int fileId, final String attId, boolean fixedSize) {
|
||||
return new AttributeOutputStream(fileId, attId, fixedSize);
|
||||
}
|
||||
|
||||
private class ContentOutputStream extends BaseOutputStream {
|
||||
|
||||
private static class ContentOutputStream extends BaseOutputStream {
|
||||
private ContentOutputStream(final int fileId, boolean readOnly) {
|
||||
super(fileId, readOnly);
|
||||
}
|
||||
@@ -1095,7 +1112,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
private class AttributeOutputStream extends BaseOutputStream {
|
||||
private static class AttributeOutputStream extends BaseOutputStream {
|
||||
private final String myAttributeId;
|
||||
|
||||
private AttributeOutputStream(final int fileId, final String attributeId, boolean fixedSize) {
|
||||
@@ -1163,7 +1180,7 @@ public class FSRecords implements Forceable {
|
||||
protected abstract AbstractStorage getStorage();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
public static void dispose() {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
DbConnection.force();
|
||||
@@ -1180,7 +1197,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
public static void checkSanity() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
//long startTime = System.currentTimeMillis();
|
||||
synchronized (lock) {
|
||||
final int fileLength = (int)getRecords().length();
|
||||
assert fileLength % RECORD_SIZE == 0;
|
||||
@@ -1201,7 +1218,7 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
//long endTime = System.currentTimeMillis();
|
||||
//System.out.println("Sanity check took " + (endTime-startTime) + " ms");
|
||||
}
|
||||
|
||||
@@ -1227,26 +1244,15 @@ public class FSRecords implements Forceable {
|
||||
}
|
||||
|
||||
private static void checkContentsStorageSanity(int id) {
|
||||
try {
|
||||
int recordId = getContentRecordId(id);
|
||||
assert recordId >= 0;
|
||||
if (recordId > 0) {
|
||||
getContentStorage().checkSanity(recordId);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw DbConnection.handleError(e);
|
||||
int recordId = getContentRecordId(id);
|
||||
assert recordId >= 0;
|
||||
if (recordId > 0) {
|
||||
getContentStorage().checkSanity(recordId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAttributesStorageSanity(int id, IntArrayList usedAttributeRecordIds, IntArrayList validAttributeIds) {
|
||||
int attributeRecordId;
|
||||
try {
|
||||
attributeRecordId = getAttributeRecordId(id);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw DbConnection.handleError(ex);
|
||||
}
|
||||
int attributeRecordId = getAttributeRecordId(id);
|
||||
|
||||
assert attributeRecordId >= 0;
|
||||
if (attributeRecordId > 0) {
|
||||
@@ -1267,7 +1273,7 @@ public class FSRecords implements Forceable {
|
||||
final DataInputStream dataInputStream = getAttributesStorage().readStream(attributeRecordId);
|
||||
try {
|
||||
final int streamSize = dataInputStream.available();
|
||||
assert (streamSize % 8) == 0;
|
||||
assert streamSize % 8 == 0;
|
||||
for (int i = 0; i < streamSize / 8; i++) {
|
||||
int attId = dataInputStream.readInt();
|
||||
int attDataRecordId = dataInputStream.readInt();
|
||||
|
||||
+91
-77
@@ -23,6 +23,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.ApplicationEx;
|
||||
import com.intellij.openapi.components.ApplicationComponent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.ShutDownTracker;
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
|
||||
import com.intellij.openapi.util.io.ByteSequence;
|
||||
@@ -59,7 +60,6 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
|
||||
public static final long FILE_LENGTH_TO_CACHE_THRESHOLD = 20 * 1024 * 1024; // 20 megabytes
|
||||
|
||||
private final FSRecords myRecords;
|
||||
private final MessageBus myEventsBus;
|
||||
|
||||
private final Map<String, NewVirtualFile> myRoots = new HashMap<String, NewVirtualFile>();
|
||||
@@ -73,7 +73,6 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
|
||||
public PersistentFS(MessageBus bus) {
|
||||
myEventsBus = bus;
|
||||
myRecords = new FSRecords();
|
||||
|
||||
/*
|
||||
PropertiesComponent properties = PropertiesComponent.getInstance();
|
||||
@@ -100,7 +99,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
private void performShutdown() {
|
||||
if (!myShutdownPerformed.getAndSet(Boolean.TRUE)) {
|
||||
LOG.info("VFS dispose started");
|
||||
myRecords.dispose();
|
||||
FSRecords.dispose();
|
||||
LOG.info("VFS dispose completed");
|
||||
}
|
||||
}
|
||||
@@ -112,7 +111,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
|
||||
public void initComponent() {
|
||||
myRecords.connect();
|
||||
FSRecords.connect();
|
||||
}
|
||||
|
||||
public boolean areChildrenLoaded(final VirtualFile dir) {
|
||||
@@ -120,7 +119,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
|
||||
public long getCreationTimestamp() {
|
||||
return myRecords.getCreationTimestamp();
|
||||
return FSRecords.getCreationTimestamp();
|
||||
}
|
||||
|
||||
private static NewVirtualFileSystem getDelegate(VirtualFile file) {
|
||||
@@ -128,25 +127,25 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
|
||||
public boolean wereChildrenAccessed(final VirtualFile dir) {
|
||||
return myRecords.wereChildrenAccessed(getFileId(dir));
|
||||
return FSRecords.wereChildrenAccessed(getFileId(dir));
|
||||
}
|
||||
|
||||
public String[] list(final VirtualFile file) {
|
||||
int id = getFileId(file);
|
||||
int[] childrenIds = FSRecords.list(id);
|
||||
String[] names = listPersisted(childrenIds);
|
||||
if (areChildrenLoaded(id)) {
|
||||
return listPersisted(file);
|
||||
}
|
||||
else {
|
||||
return persistAllChildren(file);
|
||||
return names;
|
||||
}
|
||||
Pair<String[], int[]> pair = persistAllChildren(file, id, Pair.create(names, childrenIds));
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
public String[] listPersisted(final VirtualFile file) {
|
||||
return listPersisted(getFileId(file));
|
||||
return listPersisted(FSRecords.list(getFileId(file)));
|
||||
}
|
||||
|
||||
private String[] listPersisted(final int id) {
|
||||
final int[] childrenIds = myRecords.list(id);
|
||||
private static String[] listPersisted(int[] childrenIds) {
|
||||
String[] names = ArrayUtil.newStringArray(childrenIds.length);
|
||||
for (int i = 0; i < childrenIds.length; i++) {
|
||||
names[i] = FSRecords.getName(childrenIds[i]);
|
||||
@@ -154,14 +153,15 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
return names;
|
||||
}
|
||||
|
||||
private String[] persistAllChildren(final VirtualFile file) {
|
||||
int id = getFileId(file);
|
||||
String[] currentNames = listPersisted(file);
|
||||
int[] currentIds = myRecords.list(id);
|
||||
private static Pair<String[],int[]> persistAllChildren(final VirtualFile file, int id, Pair<String[],int[]> current) {
|
||||
String[] currentNames = current.first;
|
||||
int[] currentIds = current.second;
|
||||
|
||||
final NewVirtualFileSystem delegate = getDelegate(file);
|
||||
String[] delegateNames = VfsUtil.filterNames(delegate.list(file));
|
||||
if (delegateNames.length == 0 && currentNames.length > 0) return currentNames;
|
||||
if (delegateNames.length == 0 && currentNames.length > 0) {
|
||||
return current;
|
||||
}
|
||||
|
||||
final String[] names;
|
||||
if (currentNames.length == 0) {
|
||||
@@ -189,21 +189,34 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
}
|
||||
|
||||
myRecords.updateList(id, childrenIds);
|
||||
FSRecords.updateList(id, childrenIds);
|
||||
int flags = FSRecords.getFlags(id);
|
||||
myRecords.setFlags(id, flags | CHILDREN_CACHED_FLAG, true);
|
||||
FSRecords.setFlags(id, flags | CHILDREN_CACHED_FLAG, true);
|
||||
|
||||
return names;
|
||||
return Pair.create(names, childrenIds);
|
||||
}
|
||||
|
||||
public int[] listIds(VirtualFile parent) {
|
||||
public static int[] listIds(VirtualFile parent) {
|
||||
final int parentId = getFileId(parent);
|
||||
|
||||
int[] ids = FSRecords.list(parentId);
|
||||
if (!areChildrenLoaded(parentId)) {
|
||||
list(parent);
|
||||
String[] names = listPersisted(ids);
|
||||
Pair<String[], int[]> pair = persistAllChildren(parent, parentId, Pair.create(names, ids));
|
||||
return pair.second;
|
||||
}
|
||||
|
||||
return myRecords.list(parentId);
|
||||
return ids;
|
||||
}
|
||||
public static Pair<String[],int[]> listAll(VirtualFile parent) {
|
||||
final int parentId = getFileId(parent);
|
||||
|
||||
Pair<String[], int[]> pair = FSRecords.listAll(parentId);
|
||||
if (!areChildrenLoaded(parentId)) {
|
||||
return persistAllChildren(parent, parentId, pair);
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
|
||||
@@ -214,33 +227,33 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
|
||||
@Nullable
|
||||
public DataInputStream readAttribute(final VirtualFile file, final FileAttribute att) {
|
||||
return myRecords.readAttribute(getFileId(file), att.getId());
|
||||
return FSRecords.readAttribute(getFileId(file), att.getId());
|
||||
}
|
||||
|
||||
public DataOutputStream writeAttribute(final VirtualFile file, final FileAttribute att) {
|
||||
return myRecords.writeAttribute(getFileId(file), att.getId(), att.isFixedSize());
|
||||
return FSRecords.writeAttribute(getFileId(file), att.getId(), att.isFixedSize());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DataInputStream readContent(VirtualFile file) {
|
||||
return myRecords.readContent(getFileId(file));
|
||||
private static DataInputStream readContent(VirtualFile file) {
|
||||
return FSRecords.readContent(getFileId(file));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DataInputStream readContentById(int contentId) {
|
||||
return myRecords.readContentById(contentId);
|
||||
private static DataInputStream readContentById(int contentId) {
|
||||
return FSRecords.readContentById(contentId);
|
||||
}
|
||||
|
||||
private DataOutputStream writeContent(VirtualFile file, boolean readOnly) {
|
||||
return myRecords.writeContent(getFileId(file), readOnly);
|
||||
private static DataOutputStream writeContent(VirtualFile file, boolean readOnly) {
|
||||
return FSRecords.writeContent(getFileId(file), readOnly);
|
||||
}
|
||||
|
||||
private void writeContent(VirtualFile file, ByteSequence content, boolean readOnly) throws IOException {
|
||||
myRecords.writeContent(getFileId(file), content, readOnly);
|
||||
private static void writeContent(VirtualFile file, ByteSequence content, boolean readOnly) throws IOException {
|
||||
FSRecords.writeContent(getFileId(file), content, readOnly);
|
||||
}
|
||||
|
||||
public int storeUnlinkedContent(byte[] bytes) {
|
||||
return myRecords.storeUnlinkedContent(bytes);
|
||||
return FSRecords.storeUnlinkedContent(bytes);
|
||||
}
|
||||
|
||||
public int getModificationCount(final VirtualFile file) {
|
||||
@@ -257,7 +270,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
return FSRecords.getModCount();
|
||||
}
|
||||
|
||||
private void copyRecordFromDelegateFS(final int id, final int parentId, final VirtualFile file, NewVirtualFileSystem delegate) {
|
||||
private static void copyRecordFromDelegateFS(final int id, final int parentId, final VirtualFile file, NewVirtualFileSystem delegate) {
|
||||
if (id == parentId) {
|
||||
LOG.error("Cyclic parent-child relations for file: " + file);
|
||||
return;
|
||||
@@ -271,13 +284,13 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
if (areChildrenLoaded(id)) return;
|
||||
}
|
||||
|
||||
myRecords.setParent(id, parentId);
|
||||
myRecords.setName(id, name);
|
||||
FSRecords.setParent(id, parentId);
|
||||
FSRecords.setName(id, name);
|
||||
|
||||
myRecords.setTimestamp(id, delegate.getTimeStamp(file));
|
||||
myRecords.setFlags(id, (delegate.isDirectory(file) ? IS_DIRECTORY_FLAG : 0) | (delegate.isWritable(file) ? 0 : IS_READ_ONLY), true);
|
||||
FSRecords.setTimestamp(id, delegate.getTimeStamp(file));
|
||||
FSRecords.setFlags(id, (delegate.isDirectory(file) ? IS_DIRECTORY_FLAG : 0) | (delegate.isWritable(file) ? 0 : IS_READ_ONLY), true);
|
||||
|
||||
myRecords.setLength(id, -1L);
|
||||
FSRecords.setLength(id, -1L);
|
||||
|
||||
// TODO!!!: More attributes?
|
||||
}
|
||||
@@ -313,7 +326,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
public void setTimeStamp(final VirtualFile file, final long modstamp) throws IOException {
|
||||
final int id = getFileId(file);
|
||||
|
||||
myRecords.setTimestamp(id, modstamp);
|
||||
FSRecords.setTimestamp(id, modstamp);
|
||||
getDelegate(file).setTimeStamp(file, modstamp);
|
||||
}
|
||||
|
||||
@@ -340,7 +353,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
final NewVirtualFileSystem delegate = getDelegate(parent);
|
||||
final int parentId = getFileId(parent);
|
||||
|
||||
final int[] children = myRecords.list(parentId);
|
||||
final int[] children = FSRecords.list(parentId);
|
||||
for (final int childId : children) {
|
||||
if (namesEqual(delegate, childName, FSRecords.getName(childId))) return childId;
|
||||
}
|
||||
@@ -349,7 +362,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
if (delegate.exists(fake)) {
|
||||
int child = FSRecords.createRecord();
|
||||
copyRecordFromDelegateFS(child, parentId, fake, delegate);
|
||||
myRecords.updateList(parentId, ArrayUtil.append(children, child));
|
||||
FSRecords.updateList(parentId, ArrayUtil.append(children, child));
|
||||
return child;
|
||||
}
|
||||
|
||||
@@ -362,7 +375,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
long len = FSRecords.getLength(id);
|
||||
if (len == -1) {
|
||||
len = (int)getDelegate(file).getLength(file);
|
||||
myRecords.setLength(id, len);
|
||||
FSRecords.setLength(id, len);
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -436,12 +449,12 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
final byte[] content = delegate.contentsToByteArray(file);
|
||||
|
||||
ApplicationEx application = (ApplicationEx)ApplicationManager.getApplication();
|
||||
if ((!delegate.isReadOnly() || (!application.isInternal() && !application.isUnitTestMode())) &&
|
||||
if ((!delegate.isReadOnly() || !application.isInternal() && !application.isUnitTestMode()) &&
|
||||
!noCaching && content.length <= FILE_LENGTH_TO_CACHE_THRESHOLD) {
|
||||
synchronized (INPUT_LOCK) {
|
||||
writeContent(file, new ByteSequence(content), delegate.isReadOnly());
|
||||
|
||||
myRecords.setLength(getFileId(file), content.length);
|
||||
FSRecords.setLength(getFileId(file), content.length);
|
||||
setFlag(file, MUST_RELOAD_CONTENT, false);
|
||||
}
|
||||
}
|
||||
@@ -490,7 +503,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
synchronized (INPUT_LOCK) {
|
||||
if (getBytesRead() == len) {
|
||||
writeContent(file, new ByteSequence(cache.getInternalBuffer(), 0, cache.size()), readOnly);
|
||||
myRecords.setLength(getFileId(file), len);
|
||||
FSRecords.setLength(getFileId(file), len);
|
||||
setFlag(file, MUST_RELOAD_CONTENT, false);
|
||||
}
|
||||
else {
|
||||
@@ -553,15 +566,15 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
|
||||
public int acquireContent(VirtualFile file) {
|
||||
return myRecords.acquireFileContent(getFileId(file));
|
||||
return FSRecords.acquireFileContent(getFileId(file));
|
||||
}
|
||||
|
||||
public void releaseContent(int contentId) {
|
||||
myRecords.releaseContent(contentId);
|
||||
FSRecords.releaseContent(contentId);
|
||||
}
|
||||
|
||||
public int getCurrentContentId(VirtualFile file) {
|
||||
return myRecords.getContentId(getFileId(file));
|
||||
return FSRecords.getContentId(getFileId(file));
|
||||
}
|
||||
|
||||
public void moveFile(final Object requestor, final VirtualFile file, final VirtualFile newParent) throws IOException {
|
||||
@@ -622,11 +635,12 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
|
||||
events = validateEvents(events);
|
||||
|
||||
myEventsBus.syncPublisher(VirtualFileManager.VFS_CHANGES).before(events);
|
||||
BulkFileListener publisher = myEventsBus.syncPublisher(VirtualFileManager.VFS_CHANGES);
|
||||
publisher.before(events);
|
||||
for (VFileEvent event : events) {
|
||||
applyEvent(event);
|
||||
}
|
||||
myEventsBus.syncPublisher(VirtualFileManager.VFS_CHANGES).after(events);
|
||||
publisher.after(events);
|
||||
}
|
||||
|
||||
public static final Object LOCK = new Object();
|
||||
@@ -641,7 +655,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
if (root == null) {
|
||||
try {
|
||||
final int rootId = myRecords.findRootRecord(rootUrl);
|
||||
final int rootId = FSRecords.findRootRecord(rootUrl);
|
||||
if (basePath.length() > 0) {
|
||||
root = new VirtualDirectoryImpl(basePath, null, fs, rootId);
|
||||
}
|
||||
@@ -819,7 +833,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
return "PersistentFS";
|
||||
}
|
||||
|
||||
private void executeCreateChild(final VirtualFile parent, final String name) {
|
||||
private static void executeCreateChild(final VirtualFile parent, final String name) {
|
||||
final NewVirtualFileSystem delegate = getDelegate(parent);
|
||||
VirtualFile fakeFile = new FakeVirtualFile(parent, name);
|
||||
if (delegate.exists(fakeFile)) {
|
||||
@@ -833,10 +847,10 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private void appendIdToParentList(final int parentId, final int childId) {
|
||||
int[] childrenlist = myRecords.list(parentId);
|
||||
private static void appendIdToParentList(final int parentId, final int childId) {
|
||||
int[] childrenlist = FSRecords.list(parentId);
|
||||
childrenlist = ArrayUtil.append(childrenlist, childId);
|
||||
myRecords.updateList(parentId, childrenlist);
|
||||
FSRecords.updateList(parentId, childrenlist);
|
||||
}
|
||||
|
||||
private void executeDelete(final VirtualFile file) {
|
||||
@@ -851,7 +865,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
final VirtualFile parent = file.getParent();
|
||||
final int parentId = parent != null ? getFileId(parent) : 0;
|
||||
|
||||
myRecords.deleteRecordRecursively(id);
|
||||
FSRecords.deleteRecordRecursively(id);
|
||||
|
||||
if (parentId != 0) {
|
||||
removeIdFromParentList(parentId, id);
|
||||
@@ -864,7 +878,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
synchronized (LOCK) {
|
||||
myRoots.remove(file.getUrl());
|
||||
try {
|
||||
myRecords.deleteRootRecord(id);
|
||||
FSRecords.deleteRootRecord(id);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -884,32 +898,32 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeIdFromParentList(final int parentId, final int id) {
|
||||
int[] childList = myRecords.list(parentId);
|
||||
private static void removeIdFromParentList(final int parentId, final int id) {
|
||||
int[] childList = FSRecords.list(parentId);
|
||||
childList = ArrayUtil.remove(childList, ArrayUtil.indexOf(childList, id));
|
||||
myRecords.updateList(parentId, childList);
|
||||
FSRecords.updateList(parentId, childList);
|
||||
}
|
||||
|
||||
private void executeRename(final VirtualFile file, final String newName) {
|
||||
private static void executeRename(final VirtualFile file, final String newName) {
|
||||
((VirtualFileSystemEntry)file).setName(newName);
|
||||
final int id = getFileId(file);
|
||||
myRecords.setName(id, newName);
|
||||
FSRecords.setName(id, newName);
|
||||
}
|
||||
|
||||
private void executeSetWritable(final VirtualFile file, final boolean writableFlag) {
|
||||
private static void executeSetWritable(final VirtualFile file, final boolean writableFlag) {
|
||||
setFlag(file, IS_READ_ONLY, !writableFlag);
|
||||
}
|
||||
|
||||
private void setFlag(VirtualFile file, int mask, boolean value) {
|
||||
private static void setFlag(VirtualFile file, int mask, boolean value) {
|
||||
setFlag(getFileId(file), mask, value);
|
||||
}
|
||||
|
||||
private void setFlag(final int id, final int mask, final boolean value) {
|
||||
private static void setFlag(final int id, final int mask, final boolean value) {
|
||||
int oldFlags = FSRecords.getFlags(id);
|
||||
int flags = value ? oldFlags | mask : oldFlags & ~mask;
|
||||
|
||||
if (oldFlags != flags) {
|
||||
myRecords.setFlags(id, flags, true);
|
||||
FSRecords.setFlags(id, flags, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -917,24 +931,24 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
return (FSRecords.getFlags(getFileId(file)) & mask) != 0;
|
||||
}
|
||||
|
||||
private void executeTouch(final VirtualFile file, boolean reloadContentFromDelegate, long newModificationStamp) {
|
||||
private static void executeTouch(final VirtualFile file, boolean reloadContentFromDelegate, long newModificationStamp) {
|
||||
if (reloadContentFromDelegate) {
|
||||
setFlag(file, MUST_RELOAD_CONTENT, true);
|
||||
}
|
||||
|
||||
final NewVirtualFileSystem delegate = getDelegate(file);
|
||||
myRecords.setLength(getFileId(file), delegate.getLength(file));
|
||||
myRecords.setTimestamp(getFileId(file), delegate.getTimeStamp(file));
|
||||
FSRecords.setLength(getFileId(file), delegate.getLength(file));
|
||||
FSRecords.setTimestamp(getFileId(file), delegate.getTimeStamp(file));
|
||||
|
||||
((NewVirtualFile)file).setModificationStamp(newModificationStamp);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
private void executeCopy(final VirtualFile from, final VirtualFile newParent, final String copyName) {
|
||||
private static void executeCopy(final VirtualFile from, final VirtualFile newParent, final String copyName) {
|
||||
executeCreateChild(newParent, copyName);
|
||||
}
|
||||
|
||||
private void executeMove(final VirtualFile what, final VirtualFile newParent) {
|
||||
private static void executeMove(final VirtualFile what, final VirtualFile newParent) {
|
||||
final int whatId = getFileId(what);
|
||||
final int newParentId = getFileId(newParent);
|
||||
final int oldParentId = getFileId(what.getParent());
|
||||
@@ -943,7 +957,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
appendIdToParentList(newParentId, whatId);
|
||||
|
||||
((VirtualFileSystemEntry)what).setParent(newParent);
|
||||
myRecords.setParent(whatId, newParentId);
|
||||
FSRecords.setParent(whatId, newParentId);
|
||||
}
|
||||
|
||||
public String getName(final int id) {
|
||||
@@ -953,7 +967,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
|
||||
public void cleanPersistedContents() {
|
||||
try {
|
||||
final int[] roots = myRecords.listRoots();
|
||||
final int[] roots = FSRecords.listRoots();
|
||||
for (int root : roots) {
|
||||
cleanPersistedContentsRecursively(root);
|
||||
}
|
||||
@@ -965,7 +979,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
|
||||
private void cleanPersistedContentsRecursively(int id) {
|
||||
if (isDirectory(id)) {
|
||||
for (int child : myRecords.list(id)) {
|
||||
for (int child : FSRecords.list(id)) {
|
||||
cleanPersistedContentsRecursively(child);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ public class RefreshWorker {
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> names = dir.getSuspicousNames();
|
||||
final List<String> names = dir.getSuspiciousNames();
|
||||
for (String name : names) {
|
||||
if (name.length() == 0) continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user