extract MapReduceIndexBase (doesn't depend on VFS & IDE's components) from MapReduceIndex

This commit is contained in:
Dmitry Batkovich
2016-11-16 13:04:17 +03:00
parent a97039fadb
commit d931df063c
33 changed files with 1180 additions and 782 deletions
@@ -25,12 +25,10 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectAndLibrariesScope;
import com.intellij.psi.search.ProjectScopeImpl;
import com.intellij.util.Processor;
import com.intellij.util.Processors;
import com.intellij.util.SystemProperties;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.SLRUCache;
import com.intellij.util.io.*;
import com.intellij.util.io.DataOutputStream;
import gnu.trove.TIntHashSet;
@@ -39,31 +37,18 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Eugene Zhuravlev
* Date: Dec 20, 2007
*/
public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Value>{
public final class MapIndexStorage<Key, Value> extends MapIndexStorageBase<Key, Value> implements VfsAwareIndexStorage<Key, Value> {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.indexing.MapIndexStorage");
private static final boolean ENABLE_CACHED_HASH_IDS = SystemProperties.getBooleanProperty("idea.index.no.cashed.hashids", true);
private final boolean myBuildKeyHashToVirtualFileMapping;
private PersistentMap<Key, ValueContainer<Value>> myMap;
private AppendableStorageBackedByResizableMappedFile myKeyHashToVirtualFileMapping;
private SLRUCache<Key, ChangeTrackingValueContainer<Value>> myCache;
private volatile int myLastScannedId;
private final File myBaseStorageFile;
private final KeyDescriptor<Key> myKeyDescriptor;
private final int myCacheSize;
private final Lock l = new ReentrantLock();
private final DataExternalizer<Value> myDataExternalizer;
private final boolean myKeyIsUniqueForIndexedFile;
private static final ConcurrentIntObjectMap<Boolean> ourInvalidatedSessionIds = ContainerUtil.createConcurrentIntObjectMap();
public MapIndexStorage(@NotNull File storageFile,
@@ -80,85 +65,18 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
final int cacheSize,
boolean keyIsUniqueForIndexedFile,
boolean buildKeyHashToVirtualFileMapping) throws IOException {
myBaseStorageFile = storageFile;
myKeyDescriptor = keyDescriptor;
myCacheSize = cacheSize;
myDataExternalizer = valueExternalizer;
myKeyIsUniqueForIndexedFile = keyIsUniqueForIndexedFile;
super(storageFile, keyDescriptor, valueExternalizer, cacheSize, keyIsUniqueForIndexedFile);
myBuildKeyHashToVirtualFileMapping = buildKeyHashToVirtualFileMapping && FileBasedIndex.ourEnableTracingOfKeyHashToVirtualFileMapping;
initMapAndCache();
}
private static final PersistentHashMapValueStorage.ExceptionalIOCancellationCallback ourProgressManagerCheckCancelledIOCanceller =
new PersistentHashMapValueStorage.ExceptionalIOCancellationCallback() {
@Override
public void checkCancellation() {
ProgressManager.checkCanceled();
}
};
private void initMapAndCache() throws IOException {
final ValueContainerMap<Key, Value> map;
PersistentHashMapValueStorage.CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.set(ourProgressManagerCheckCancelledIOCanceller);
PersistentHashMapValueStorage.CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.set(Boolean.TRUE);
try {
map = new ValueContainerMap<>(getStorageFile(), myKeyDescriptor, myDataExternalizer, myKeyIsUniqueForIndexedFile);
} finally {
PersistentHashMapValueStorage.CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.set(null);
PersistentHashMapValueStorage.CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.set(null);
}
myCache = new SLRUCache<Key, ChangeTrackingValueContainer<Value>>(myCacheSize, (int)(Math.ceil(myCacheSize * 0.25)) /* 25% from the main cache size*/) {
@Override
@NotNull
public ChangeTrackingValueContainer<Value> createValue(final Key key) {
return new ChangeTrackingValueContainer<>(new ChangeTrackingValueContainer.Initializer<Value>() {
@NotNull
@Override
public Object getLock() {
return map.getDataAccessLock();
}
@Nullable
@Override
public ValueContainer<Value> compute() {
ValueContainer<Value> value;
try {
value = map.get(key);
if (value == null) {
value = new ValueContainerImpl<>();
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
return value;
}
});
}
@Override
protected void onDropFromCache(final Key key, @NotNull final ChangeTrackingValueContainer<Value> valueContainer) {
if (valueContainer.isDirty()) {
try {
map.put(key, valueContainer);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
};
myMap = map;
@Override
protected void initMapAndCache() throws IOException {
super.initMapAndCache();
myKeyHashToVirtualFileMapping = myBuildKeyHashToVirtualFileMapping ?
new AppendableStorageBackedByResizableMappedFile(getProjectFile(), 4096, null, PagedFileStorage.MB, true) : null;
}
@NotNull
private File getStorageFile() {
return new File(myBaseStorageFile.getPath() + ".storage");
}
@NotNull
private File getProjectFile() {
return new File(myBaseStorageFile.getPath() + ".project");
@@ -172,14 +90,12 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
myKeyHashToVirtualFileMapping.getPagedFileStorage().unlock();
}
}
@Override
public void flush() {
l.lock();
try {
if (!myMap.isClosed()) {
myCache.clear();
if (myMap.isDirty()) myMap.force();
}
super.flush();
if (myKeyHashToVirtualFileMapping != null && myKeyHashToVirtualFileMapping.isDirty()) {
withLock(() -> myKeyHashToVirtualFileMapping.force());
}
@@ -191,57 +107,34 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
@Override
public void close() throws StorageException {
super.close();
try {
flush();
if (myKeyHashToVirtualFileMapping != null) {
withLock(() -> myKeyHashToVirtualFileMapping.close());
}
myMap.close();
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw new StorageException(cause);
}
if (cause instanceof StorageException) {
throw (StorageException)cause;
}
throw e;
unwrapCauseAndRethrow(e);
}
}
@Override
public void clear() throws StorageException{
try {
myMap.close();
if (myKeyHashToVirtualFileMapping != null) {
withLock(() -> myKeyHashToVirtualFileMapping.close());
}
}
catch (IOException|RuntimeException e) {
catch (RuntimeException e) {
LOG.error(e);
}
try {
IOUtil.deleteAllFilesStartingWith(getStorageFile());
if (myKeyHashToVirtualFileMapping != null) IOUtil.deleteAllFilesStartingWith(getProjectFile());
initMapAndCache();
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw new StorageException(cause);
}
if (cause instanceof StorageException) {
throw (StorageException)cause;
}
throw e;
unwrapCauseAndRethrow(e);
}
super.clear();
}
@Override
@@ -302,20 +195,13 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
return processor.process(key);
});
}
return myMap.processKeys(processor);
return processKeys(processor);
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw new StorageException(cause);
}
if (cause instanceof StorageException) {
throw (StorageException)cause;
}
throw e;
return unwrapCauseAndRethrow(e);
}
finally {
l.unlock();
@@ -406,36 +292,6 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
return new File(getSessionDir(), getProjectFile().getName() + "." + project.hashCode() + "." + id + "." + scope.isSearchInLibraries());
}
@NotNull
@Override
public Collection<Key> getKeys() throws StorageException {
List<Key> keys = new ArrayList<>();
processKeys(Processors.cancelableCollectProcessor(keys), null, null);
return keys;
}
@Override
@NotNull
public ChangeTrackingValueContainer<Value> read(final Key key) throws StorageException {
l.lock();
try {
return myCache.get(key);
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw new StorageException(cause);
}
if (cause instanceof StorageException) {
throw (StorageException)cause;
}
throw e;
}
finally {
l.unlock();
}
}
@Override
public void addValue(final Key key, final int inputId, final Value value) throws StorageException {
try {
@@ -447,41 +303,7 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
myLastScannedId = 0;
}
}
myMap.markDirty();
if (!myKeyIsUniqueForIndexedFile) {
read(key).addValue(inputId, value);
return;
}
ChangeTrackingValueContainer<Value> cached;
try {
l.lock();
cached = myCache.getIfCached(key);
} finally {
l.unlock();
}
if (cached != null) {
cached.addValue(inputId, value);
return;
}
// do not pollute the cache with keys unique to indexed file
ChangeTrackingValueContainer<Value> valueContainer = new ChangeTrackingValueContainer<>(null);
valueContainer.addValue(inputId, value);
myMap.put(key, valueContainer);
}
catch (IOException e) {
throw new StorageException(e);
}
}
@Override
public void removeAllValues(@NotNull Key key, int inputId) throws StorageException {
try {
myMap.markDirty();
// important: assuming the key exists in the index
read(key).removeAssociatedValue(inputId);
super.addValue(key, inputId, value);
}
catch (IOException e) {
throw new StorageException(e);
@@ -24,7 +24,6 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.openapi.util.io.ByteSequence;
@@ -46,72 +45,43 @@ import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author Eugene Zhuravlev
* Date: Dec 10, 2007
*/
public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Value, Input> {
public class MapReduceIndex<Key, Value, Input> extends MapReduceIndexBase<Key,Value, Input> implements UpdatableIndex<Key,Value, Input>{
private static final Logger LOG = Logger.getInstance("#com.intellij.util.indexing.MapReduceIndex");
private static final int NULL_MAPPING = 0;
@NotNull private final ID<Key, Value> myIndexId;
private final DataIndexer<Key, Value, Input> myIndexer;
@NotNull protected final IndexStorage<Key, Value> myStorage;
private final boolean myHasSnapshotMapping;
private final DataExternalizer<Value> myValueExternalizer;
private final DataExternalizer<Collection<Key>> mySnapshotIndexExternalizer;
private final boolean myIsPsiBackedIndex;
private final IndexExtension<Key, Value, Input> myExtension;
private final AtomicBoolean myInMemoryMode = new AtomicBoolean();
private final AtomicLong myModificationStamp = new AtomicLong();
private final TIntObjectHashMap<Collection<Key>> myInMemoryKeys = new TIntObjectHashMap<>();
private PersistentHashMap<Integer, ByteSequence> myContents;
private PersistentHashMap<Integer, Integer> myInputsSnapshotMapping;
@Nullable protected PersistentHashMap<Integer, Collection<Key>> myInputsIndex;
private PersistentHashMap<Integer, String> myIndexingTrace;
private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock();
private final LowMemoryWatcher myLowMemoryFlusher = LowMemoryWatcher.register(new Runnable() {
@Override
public void run() {
try {
if (myStorage instanceof MemoryIndexStorage) {
Lock writeLock = getWriteLock();
if (writeLock.tryLock()) {
try {
((MemoryIndexStorage<Key, Value>)myStorage).clearCaches();
} finally {
writeLock.unlock();
}
}
}
flush();
} catch (StorageException e) {
LOG.info(e);
requestRebuild(null);
}
static {
if (!DebugAssertions.DEBUG) {
final Application app = ApplicationManager.getApplication();
DebugAssertions.DEBUG = app.isEAP() || app.isInternal();
}
});
}
public MapReduceIndex(IndexExtension<Key, Value, Input> extension,
@NotNull IndexStorage<Key, Value> storage) throws IOException {
myIndexId = extension.getName();
myExtension = extension;
super(extension, storage, false);
SharedIndicesData.registerIndex(myIndexId, extension);
myIndexer = extension.getIndexer();
myStorage = storage;
myHasSnapshotMapping = extension instanceof FileBasedIndexExtension &&
((FileBasedIndexExtension<Key, Value>)extension).hasSnapshotMapping() &&
IdIndex.ourSnapshotMappingsEnabled;
mySnapshotIndexExternalizer = createInputsIndexExternalizer(extension, myIndexId, extension.getKeyDescriptor());
myValueExternalizer = extension.getValueExternalizer();
myIsPsiBackedIndex = extension instanceof PsiDependentIndex;
myContents = createContentsIndex(); // todo
@@ -146,21 +116,9 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
}
private static <K> DataExternalizer<Collection<K>> createInputsIndexExternalizer(IndexExtension<K, ?, ?> extension,
ID<K, ?> indexId,
KeyDescriptor<K> keyDescriptor) {
DataExternalizer<Collection<K>> externalizer;
if (extension instanceof CustomInputsIndexFileBasedIndexExtension) {
externalizer = ((CustomInputsIndexFileBasedIndexExtension<K>)extension).createExternalizer();
} else {
externalizer = new InputIndexDataExternalizer<>(keyDescriptor, indexId);
}
return externalizer;
}
@NotNull
private static <K> PersistentHashMap<Integer, Collection<K>> createIdToDataKeysIndex(@NotNull IndexExtension <K, ?, ?> extension,
@NotNull MemoryIndexStorage<K, ?> storage)
private <K> PersistentHashMap<Integer, Collection<K>> createIdToDataKeysIndex(@NotNull IndexExtension <K, ?, ?> extension,
@NotNull MemoryIndexStorageBase<K, ?, ?> storage)
throws IOException {
ID<K, ?> indexId = extension.getName();
KeyDescriptor<K> keyDescriptor = extension.getKeyDescriptor();
@@ -186,41 +144,19 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
}
@NotNull
public IndexStorage<Key, Value> getStorage() {
return myStorage;
}
@Override
public void clear() throws StorageException {
try {
getWriteLock().lock();
myStorage.clear();
if (myInputsIndex != null) {
cleanMapping(myInputsIndex);
myInputsIndex = createInputsIndex();
}
if (myInputsSnapshotMapping != null) {
cleanMapping(myInputsSnapshotMapping);
myInputsSnapshotMapping = createInputSnapshotMapping();
}
if (myIndexingTrace != null) {
cleanMapping(myIndexingTrace);
myIndexingTrace = createIndexingTrace();
}
if (myContents != null) {
cleanMapping(myContents);
myContents = createContentsIndex();
}
protected void cleanMappings() throws IOException {
if (myInputsSnapshotMapping != null) {
cleanMapping(myInputsSnapshotMapping);
myInputsSnapshotMapping = createInputSnapshotMapping();
}
catch (StorageException e) {
LOG.error(e);
if (myIndexingTrace != null) {
cleanMapping(myIndexingTrace);
myIndexingTrace = createIndexingTrace();
}
catch (IOException e) {
LOG.error(e);
}
finally {
getWriteLock().unlock();
if (myContents != null) {
cleanMapping(myContents);
myContents = createContentsIndex();
}
}
@@ -265,72 +201,18 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
}
private static void cleanMapping(@NotNull PersistentHashMap<?, ?> index) {
final File baseFile = index.getBaseFile();
try {
index.close();
}
catch (Throwable ignored) {
}
IOUtil.deleteAllFilesStartingWith(baseFile);
@Override
protected void doForce() {
doForce(myInputsSnapshotMapping);
doForce(myIndexingTrace);
doForce(myContents);
}
@Override
public void flush() throws StorageException{
try {
getReadLock().lock();
doForce(myInputsIndex);
doForce(myInputsSnapshotMapping);
doForce(myIndexingTrace);
doForce(myContents);
myStorage.flush();
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof StorageException || cause instanceof IOException) {
throw new StorageException(cause);
}
else {
throw e;
}
}
finally {
getReadLock().unlock();
}
}
private static void doForce(@Nullable PersistentHashMap<?, ?> inputsIndex) {
if (inputsIndex != null && inputsIndex.isDirty()) {
inputsIndex.force();
}
}
@Override
public void dispose() {
myLowMemoryFlusher.stop();
final Lock lock = getWriteLock();
try {
lock.lock();
try {
myStorage.close();
}
finally {
doClose(myInputsIndex);
doClose(myInputsSnapshotMapping);
doClose(myIndexingTrace);
doClose(myContents);
}
}
catch (StorageException e) {
LOG.error(e);
}
finally {
lock.unlock();
}
protected void doClose() {
doClose(myInputsSnapshotMapping);
doClose(myIndexingTrace);
doClose(myContents);
}
@Override
@@ -348,35 +230,12 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
return IndexingStamp.isFileIndexedStateCurrent(fileId, myIndexId);
}
private static void doClose(@Nullable PersistentHashMap<?, ?> index) {
if (index != null) {
try {
index.close();
}
catch (IOException e) {
LOG.error(e);
}
}
}
@NotNull
@Override
public final Lock getReadLock() {
return myLock.readLock();
}
@NotNull
@Override
public final Lock getWriteLock() {
return myLock.writeLock();
}
@Override
public boolean processAllKeys(@NotNull Processor<Key> processor, @NotNull GlobalSearchScope scope, IdFilter idFilter) throws StorageException {
final Lock lock = getReadLock();
try {
lock.lock();
return myStorage.processKeys(processor, scope, idFilter);
return ((VfsAwareIndexStorage<Key, Value>)myStorage).processKeys(processor, scope, idFilter);
}
finally {
lock.unlock();
@@ -384,22 +243,8 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
@Override
@NotNull
public ValueContainer<Value> getData(@NotNull final Key key) throws StorageException {
final Lock lock = getReadLock();
try {
lock.lock();
ValueContainerImpl.ourDebugIndexInfo.set(myIndexId);
return myStorage.read(key);
}
finally {
ValueContainerImpl.ourDebugIndexInfo.set(null);
lock.unlock();
}
}
protected PersistentHashMap<Integer, Collection<Key>> createInputsIndex() throws IOException {
return createIdToDataKeysIndex(myExtension, (MemoryIndexStorage<Key, ?>)myStorage);
return createIdToDataKeysIndex(myExtension, (MemoryIndexStorageBase<Key, ?, ?>)myStorage);
}
private static final boolean doReadSavedPersistentData = SystemProperties.getBooleanProperty("idea.read.saved.persistent.index", true);
@@ -457,9 +302,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
if (data == null) {
data = content != null ? myIndexer.map(content) : Collections.emptyMap();
if (DebugAssertions.DEBUG) {
checkValuesHaveProperEqualsAndHashCode(data);
}
checkValuesHaveProperEqualsAndHashCode(data);
}
if (hashId != null && !havePersistentData) {
@@ -477,7 +320,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
}
}
ProgressManager.checkCanceled();
myStorage.checkCanceled();
UpdateData<Key, Value> optimizedUpdateData = null;
final NotNullComputable<Collection<Key>> oldKeysGetter;
@@ -559,15 +402,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
throw new RuntimeException(ex);
}
} else {
oldKeysGetter = () -> {
try {
Collection<Key> oldKeys = readInputKeys(inputId);
return oldKeys == null? Collections.<Key>emptyList() : oldKeys;
}
catch (IOException e) {
throw new RuntimeException(e);
}
};
oldKeysGetter = createOldKeysGetterByInputIndex(inputId);
savedInputId = inputId;
}
@@ -580,13 +415,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
catch (StorageException|ProcessCanceledException ex) {
LOG.info("Exception during updateWithMap:" + ex);
Application application = ApplicationManager.getApplication();
if (application.isUnitTestMode() || application.isHeadlessEnvironment()) {
// avoid deadlock due to synchronous update in DumbServiceImpl#queueTask
application.invokeLater(() -> requestRebuild(ex), ModalityState.any());
} else {
requestRebuild(ex);
}
requestRebuild(ex);
return Boolean.FALSE;
}
@@ -594,15 +423,35 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
};
}
protected void requestRebuild(@Nullable Exception ex) {
if (ex == null) {
FileBasedIndex.getInstance().requestRebuild(myIndexId);
}
else {
FileBasedIndex.getInstance().requestRebuild(myIndexId, ex);
@Override
public void requestRebuild(@Nullable Exception ex) {
Runnable action = () -> {
if (ex == null) {
FileBasedIndex.getInstance().requestRebuild(myIndexId);
}
else {
FileBasedIndex.getInstance().requestRebuild(myIndexId, ex);
}
};
Application application = ApplicationManager.getApplication();
if (application.isUnitTestMode() || application.isHeadlessEnvironment()) {
// avoid deadlock due to synchronous update in DumbServiceImpl#queueTask
application.invokeLater(action, ModalityState.any());
} else {
action.run();
}
}
@Override
protected <K> DataExternalizer<Collection<K>> createInputsIndexExternalizer(IndexExtension<K, ?, ?> extension,
ID<K, ?> indexId,
KeyDescriptor<K> keyDescriptor) {
return extension instanceof CustomInputsIndexFileBasedIndexExtension ?
((CustomInputsIndexFileBasedIndexExtension<K>)extension).createExternalizer() :
super.createInputsIndexExternalizer(extension, indexId, keyDescriptor);
}
@Override
protected UpdateData<Key, Value> buildUpdateData(Map<Key, Value> data, NotNullComputable<Collection<Key>> oldKeysGetter, int savedInputId) {
return new SimpleUpdateData(myIndexId, savedInputId, data, oldKeysGetter);
}
@@ -678,7 +527,8 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
if (myInputsSnapshotMapping != null) myInputsSnapshotMapping.put(inputId, savedInputId);
}
private Collection<Key> readInputKeys(int inputId) throws IOException {
@Override
protected Collection<Key> readInputKeys(int inputId) throws IOException {
if (myInMemoryMode.get()) {
synchronized (myInMemoryKeys) {
Collection<Key> keys = myInMemoryKeys.get(inputId);
@@ -710,10 +560,11 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
return keys;
}
return myInputsIndex != null ? myInputsIndex.get(inputId) : null;
return super.readInputKeys(inputId);
}
private void saveInputKeys(int inputId, int savedInputId, Map<Key, Value> newData) throws IOException {
@Override
protected void saveInputKeys(int inputId, int savedInputId, Map<Key, Value> newData) throws IOException {
if (myInMemoryMode.get()) {
synchronized (myInMemoryKeys) {
myInMemoryKeys.put(inputId, newData.keySet());
@@ -722,14 +573,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
if (myHasSnapshotMapping) {
saveInputHashId(inputId, savedInputId);
} else {
if (myInputsIndex != null) {
if (newData.size() > 0) {
myInputsIndex.put(inputId, newData.keySet());
}
else {
myInputsIndex.remove(inputId);
}
}
super.saveInputKeys(inputId, savedInputId, newData);
if (SharedIndicesData.ourFileSharedIndicesEnabled) {
Set<Key> newKeys = newData.keySet();
@@ -740,32 +584,6 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
}
private void checkValuesHaveProperEqualsAndHashCode(Map<Key, Value> data) {
for(Map.Entry<Key, Value> e: data.entrySet()) {
final Value value = e.getValue();
if (!(Comparing.equal(value, value) && (value == null || value.hashCode() == value.hashCode()))) {
LOG.error("Index " + myIndexId.toString() + " violates equals / hashCode contract for Value parameter");
}
if (myValueExternalizer != null) {
try {
final BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream();
DataOutputStream outputStream = new DataOutputStream(out);
myValueExternalizer.save(outputStream, value);
outputStream.close();
final Value deserializedValue =
myValueExternalizer.read(new DataInputStream(new UnsyncByteArrayInputStream(out.getInternalBuffer(), 0, out.size())));
if (!(Comparing.equal(value, deserializedValue) && (value == null || value.hashCode() == deserializedValue.hashCode()))) {
LOG.error("Index " + myIndexId.toString() + " deserialization violates equals / hashCode contract for Value parameter");
}
} catch (IOException ex) {
LOG.error(ex);
}
}
}
}
private StringBuilder buildDiff(Map<Key, Value> data, Map<Key, Value> contentData) {
StringBuilder moreInfo = new StringBuilder();
if (contentData.size() != data.size()) {
@@ -906,84 +724,4 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
private static final com.intellij.openapi.util.Key<Integer> ourSavedContentHashIdKey = com.intellij.openapi.util.Key.create("saved.content.hash.id");
private static final com.intellij.openapi.util.Key<Integer> ourSavedUncommittedHashIdKey = com.intellij.openapi.util.Key.create("saved.uncommitted.hash.id");
public IndexExtension<Key, Value, Input> getExtension() {
return myExtension;
}
public long getModificationStamp() {
return myModificationStamp.get();
}
public class SimpleUpdateData extends UpdateData<Key, Value> {
private final int savedInputId;
private final @NotNull Map<Key, Value> newData;
protected final @NotNull NotNullComputable<Collection<Key>> oldKeysGetter;
public SimpleUpdateData(ID<Key,Value> indexId, int id, @NotNull Map<Key, Value> data, @NotNull NotNullComputable<Collection<Key>> getter) {
super(indexId);
savedInputId = id;
newData = data;
oldKeysGetter = getter;
}
public void iterateRemovedOrUpdatedKeys(int inputId, RemovedOrUpdatedKeyProcessor<Key> consumer) throws StorageException {
MapDiffUpdateData.iterateRemovedKeys(oldKeysGetter.compute(), inputId, consumer);
}
public void iterateAddedKeys(final int inputId, final AddedKeyProcessor<Key, Value> consumer) throws StorageException {
MapDiffUpdateData.iterateAddedKeyAndValues(inputId, consumer, newData);
}
@Override
public void save(int inputId) throws IOException {
saveInputKeys(inputId, savedInputId, newData);
}
public @NotNull Map<Key, Value> getNewData() {
return newData;
}
}
private final MapDiffUpdateData.RemovedOrUpdatedKeyProcessor<Key>
myRemoveStaleKeyOperation = new MapDiffUpdateData.RemovedOrUpdatedKeyProcessor<Key>() {
@Override
public void process(Key key, int inputId) throws StorageException {
myModificationStamp.incrementAndGet();
myStorage.removeAllValues(key, inputId);
}
};
private final MapDiffUpdateData.AddedKeyProcessor<Key, Value> myAddedKeyProcessor = new MapDiffUpdateData.AddedKeyProcessor<Key, Value>() {
@Override
public void process(Key key, Value value, int inputId) throws StorageException {
myModificationStamp.incrementAndGet();
myStorage.addValue(key, inputId, value);
}
};
protected void updateWithMap(final int inputId,
@NotNull UpdateData<Key, Value> updateData) throws StorageException {
getWriteLock().lock();
try {
try {
ValueContainerImpl.ourDebugIndexInfo.set(myIndexId);
updateData.iterateRemovedOrUpdatedKeys(inputId, myRemoveStaleKeyOperation);
updateData.iterateAddedKeys(inputId, myAddedKeyProcessor);
updateData.save(inputId);
}
catch (ProcessCanceledException pce) {
throw pce; // extra care
}
catch (Throwable e) { // e.g. IOException, AssertionError
throw new StorageException(e);
}
finally {
ValueContainerImpl.ourDebugIndexInfo.set(null);
}
}
finally {
getWriteLock().unlock();
}
}
}
@@ -18,199 +18,22 @@ package com.intellij.util.indexing;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import com.intellij.util.Processors;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.*;
/**
* This storage is needed for indexing yet unsaved data without saving those changes to 'main' backend storage
*
* @author Eugene Zhuravlev
* Date: Dec 10, 2007
*/
public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value> {
private final Map<Key, ChangeTrackingValueContainer<Value>> myMap = new HashMap<>();
@NotNull
private final IndexStorage<Key, Value> myBackendStorage;
private final List<BufferingStateListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final ID<?, ?> myIndexId;
private boolean myBufferingEnabled;
public interface BufferingStateListener {
void bufferingStateChanged(boolean newState);
void memoryStorageCleared();
}
public MemoryIndexStorage(@NotNull IndexStorage<Key, Value> backend) {
this(backend, null);
}
public MemoryIndexStorage(@NotNull IndexStorage<Key, Value> backend, ID<?, ?> indexId) {
myBackendStorage = backend;
myIndexId = indexId;
}
@NotNull
public IndexStorage<Key, Value> getBackendStorage() {
return myBackendStorage;
}
public void addBufferingStateListener(@NotNull BufferingStateListener listener) {
myListeners.add(listener);
}
public void removeBufferingStateListener(@NotNull BufferingStateListener listener) {
myListeners.remove(listener);
}
public void setBufferingEnabled(boolean enabled) {
final boolean wasEnabled = myBufferingEnabled;
assert wasEnabled != enabled;
myBufferingEnabled = enabled;
for (BufferingStateListener listener : myListeners) {
listener.bufferingStateChanged(enabled);
}
}
public boolean isBufferingEnabled() {
return myBufferingEnabled;
}
public void clearMemoryMap() {
myMap.clear();
}
public void fireMemoryStorageCleared() {
for (BufferingStateListener listener : myListeners) {
listener.memoryStorageCleared();
}
}
void clearCaches() {
if (myMap.size() == 0) return;
if (DebugAssertions.DEBUG) {
String message = "Dropping caches for " + (myIndexId != null ? myIndexId:this) + ", number of items:" + myMap.size();
FileBasedIndexImpl.LOG.info(message);
}
for(ChangeTrackingValueContainer<Value> v:myMap.values()) {
v.dropMergedData();
}
}
@Override
public void close() throws StorageException {
myBackendStorage.close();
}
@Override
public void clear() throws StorageException {
clearMemoryMap();
myBackendStorage.clear();
}
@Override
public void flush() throws IOException {
myBackendStorage.flush();
}
@NotNull
@Override
public Collection<Key> getKeys() throws StorageException {
final Set<Key> keys = new HashSet<>();
processKeys(Processors.cancelableCollectProcessor(keys), null, null);
return keys;
public class MemoryIndexStorage<Key, Value> extends MemoryIndexStorageBase<Key, Value, VfsAwareIndexStorage<Key, Value>> implements VfsAwareIndexStorage<Key, Value> {
public MemoryIndexStorage(@NotNull VfsAwareIndexStorage<Key, Value> backend, ID<?, ?> indexId) {
super(backend, indexId);
}
@Override
public boolean processKeys(@NotNull final Processor<Key> processor, GlobalSearchScope scope, IdFilter idFilter) throws StorageException {
final Set<Key> stopList = new HashSet<>();
Processor<Key> decoratingProcessor = key -> {
if (stopList.contains(key)) return true;
final UpdatableValueContainer<Value> container = myMap.get(key);
if (container != null && container.size() == 0) {
return true;
}
return processor.process(key);
};
for (Key key : myMap.keySet()) {
if (!decoratingProcessor.process(key)) {
return false;
}
stopList.add(key);
}
return myBackendStorage.processKeys(stopList.isEmpty() && myMap.isEmpty() ? processor : decoratingProcessor, scope, idFilter);
}
@Override
public void addValue(final Key key, final int inputId, final Value value) throws StorageException {
if (myBufferingEnabled) {
getMemValueContainer(key).addValue(inputId, value);
return;
}
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.dropMergedData();
}
myBackendStorage.addValue(key, inputId, value);
}
@Override
public void removeAllValues(@NotNull Key key, int inputId) throws StorageException {
if (myBufferingEnabled) {
getMemValueContainer(key).removeAssociatedValue(inputId);
return;
}
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.dropMergedData();
}
myBackendStorage.removeAllValues(key, inputId);
}
private UpdatableValueContainer<Value> getMemValueContainer(final Key key) {
ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer == null) {
valueContainer = new ChangeTrackingValueContainer<>(new ChangeTrackingValueContainer.Initializer<Value>() {
@Override
public Object getLock() {
return this;
}
@Override
public ValueContainer<Value> compute() {
try {
return myBackendStorage.read(key);
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
});
myMap.put(key, valueContainer);
}
return valueContainer;
}
@Override
@NotNull
public ValueContainer<Value> read(final Key key) throws StorageException {
final ValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
return valueContainer;
}
return myBackendStorage.read(key);
final Processor<Key> uniqueResultProcessor = processCacheFirstProcessor(processor);
return uniqueResultProcessor != null && myBackendStorage.processKeys(uniqueResultProcessor, scope, idFilter);
}
}
@@ -16,39 +16,33 @@
package com.intellij.util.indexing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.locks.Lock;
/**
* @author Eugene Zhuravlev
* Date: Dec 10, 2007
*/
public interface UpdatableIndex<Key, Value, Input> extends AbstractIndex<Key,Value> {
void clear() throws StorageException;
void flush() throws StorageException;
/**
* @param inputId *positive* id of content.
*/
@NotNull
Computable<Boolean> update(int inputId, @Nullable Input content);
@NotNull
Lock getReadLock();
@NotNull
Lock getWriteLock();
void dispose();
public interface UpdatableIndex<Key, Value, Input> extends AbstractUpdatableIndex<Key,Value, Input> {
boolean processAllKeys(@NotNull Processor<Key> processor, GlobalSearchScope scope, @Nullable IdFilter idFilter) throws StorageException;
void setIndexedStateForFile(int fileId, @NotNull VirtualFile file);
void resetIndexedStateForFile(int fileId);
boolean isIndexedStateForFile(int fileId, @NotNull VirtualFile file);
@Override
default boolean processAllKeys(@NotNull Processor<Key> processor) throws StorageException {
return processAllKeys(processor, null, null);
}
@NotNull
@Override
default Class<? extends RuntimeException> getProcessCanceledExceptionClass() {
return ProcessCanceledException.class;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,21 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.indexing;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene Zhuravlev
* Date: Dec 24, 2007
*/
public interface AbstractIndex<Key, Value> {
@NotNull
ValueContainer<Value> getData(@NotNull Key key) throws StorageException;
public interface VfsAwareIndexStorage<Key, Value> extends IndexStorage<Key, Value> {
boolean processAllKeys(@NotNull Processor<Key> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter idFilter) throws StorageException;
boolean processKeys(@NotNull Processor<Key> processor, GlobalSearchScope scope, @Nullable IdFilter idFilter) throws StorageException;
@Override
default void checkCanceled() {
ProgressManager.checkCanceled();
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.indexing;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.locks.Lock;
public interface AbstractUpdatableIndex<Key, Value, Input> {
void clear() throws StorageException;
void flush() throws StorageException;
/**
* @param inputId *positive* id of content.
*/
@NotNull
Computable<Boolean> update(int inputId, @Nullable Input content);
@NotNull
ValueContainer<Value> getData(@NotNull Key key) throws StorageException;
boolean processAllKeys(@NotNull Processor<Key> processor) throws StorageException;
@NotNull
Lock getReadLock();
@NotNull
Lock getWriteLock();
void dispose();
void requestRebuild(@Nullable Exception ex);
@NotNull
Class<? extends RuntimeException> getProcessCanceledExceptionClass();
}
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.indexing;
import com.intellij.util.CommonProcessors;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
class CancelableCollectProcessor<T> extends CommonProcessors.CollectProcessor<T> {
private final Runnable myCancelChecker;
public CancelableCollectProcessor(@NotNull Collection<T> collection, @NotNull Runnable cancelChecker) {
super(collection);
myCancelChecker = cancelChecker;
}
@Override
public boolean process(T o) {
myCancelChecker.run();
return super.process(o);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ class ChangeTrackingValueContainer<Value> extends UpdatableValueContainer<Value>
merged.addValue(inputId, value);
}
if (myAdded == null) myAdded = new ValueContainerImpl<>();
if (myAdded == null) myAdded = new ValueContainerImpl<Value>();
myAdded.addValue(inputId, value);
}
@@ -131,7 +131,7 @@ class ChangeTrackingValueContainer<Value> extends UpdatableValueContainer<Value>
(newMerged.size() > ValueContainerImpl.NUMBER_OF_VALUES_THRESHOLD ||
(myAdded != null && myAdded.size() > ValueContainerImpl.NUMBER_OF_VALUES_THRESHOLD))) {
// Calculate file ids that have Value mapped to avoid O(NumberOfValuesInMerged) during removal
fileId2ValueMapping = new FileId2ValueMapping<>(newMerged);
fileId2ValueMapping = new FileId2ValueMapping<Value>(newMerged);
}
final FileId2ValueMapping<Value> finalFileId2ValueMapping = fileId2ValueMapping;
if (myInvalidated != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
*/
package com.intellij.util.indexing;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.hash.LinkedHashMap;
@@ -27,9 +26,10 @@ import java.util.Formatter;
public class DebugAssertions {
private static final Logger LOG = Logger.getInstance(DebugAssertions.class);
public static final boolean DEBUG = SystemProperties.getBooleanProperty(
@SuppressWarnings("StaticNonFinalField")
public static volatile boolean DEBUG = SystemProperties.getBooleanProperty(
"intellij.idea.indices.debug",
ApplicationManager.getApplication().isInternal() || ApplicationManager.getApplication().isEAP()
false
);
public static final boolean EXTRA_SANITY_CHECKS = SystemProperties.getBooleanProperty(
@@ -56,9 +56,9 @@ public class DebugAssertions {
static <Key> boolean equals(Collection<Key> keys, Collection<Key> keys2, KeyDescriptor<Key> keyDescriptor) {
if (keys == null && keys2 == null) return true;
if (keys == null || keys2 == null || keys.size() != keys2.size()) return false;
LinkedHashMap<Key, Boolean> map = new LinkedHashMap<>(keys.size(), 0.8f, keyDescriptor);
LinkedHashMap<Key, Boolean> map = new LinkedHashMap<Key, Boolean>(keys.size(), 0.8f, keyDescriptor);
for(Key key:keys) map.put(key, Boolean.TRUE);
LinkedHashMap<Key, Boolean> map2 = new LinkedHashMap<>(keys.size(), 0.8f, keyDescriptor);
LinkedHashMap<Key, Boolean> map2 = new LinkedHashMap<Key, Boolean>(keys.size(), 0.8f, keyDescriptor);
for(Key key:keys2) map2.put(key, Boolean.TRUE);
return map.equals(map2);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ class FileId2ValueMapping<Value> {
private boolean myOnePerFileValidationEnabled = true;
FileId2ValueMapping(ValueContainerImpl<Value> _valueContainer) {
id2ValueMap = new TIntObjectHashMap<>();
id2ValueMap = new TIntObjectHashMap<Value>();
valueContainer = _valueContainer;
TIntArrayList removedFileIdList = null;
@@ -45,7 +45,7 @@ class FileId2ValueMapping<Value> {
if (previousValue != null) { // delay removal of duplicated id -> value mapping since it will affect valueIterator we are using
if (removedFileIdList == null) {
removedFileIdList = new TIntArrayList();
removedValueList = new SmartList<>();
removedValueList = new SmartList<Value>();
}
removedFileIdList.add(id);
removedValueList.add(previousValue);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,8 @@
package com.intellij.util.indexing;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Flushable;
import java.io.IOException;
@@ -40,7 +38,7 @@ public interface IndexStorage<Key, Value> extends Flushable {
@NotNull
ValueContainer<Value> read(Key key) throws StorageException;
boolean processKeys(@NotNull Processor<Key> processor, GlobalSearchScope scope, @Nullable IdFilter idFilter) throws StorageException;
boolean processKeys(@NotNull Processor<Key> processor) throws StorageException;
@NotNull
Collection<Key> getKeys() throws StorageException;
@@ -49,4 +47,6 @@ public interface IndexStorage<Key, Value> extends Flushable {
@Override
void flush() throws IOException;
void checkCanceled();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ public class InputIndexDataExternalizer<K> implements DataExternalizer<Collectio
public Collection<K> read(@NotNull DataInput in) throws IOException {
try {
final int size = DataInputOutputUtil.readINT(in);
final List<K> list = new ArrayList<>(size);
final List<K> list = new ArrayList<K>(size);
for (int idx = 0; idx < size; idx++) {
list.add(myKeyDescriptor.read(in));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
*/
package com.intellij.util.indexing;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
@@ -44,7 +43,7 @@ public abstract class MapDiffUpdateData<Key, Value> extends UpdateData<Key, Valu
Map<Key, Value> data) throws StorageException {
if (data instanceof THashMap) {
// such map often (from IdIndex) contain 100x (avg ~240) of entries, also THashMap have no Entry inside so we optimize for gc too
final Ref<StorageException> exceptionRef = new Ref<>();
final Ref<StorageException> exceptionRef = new Ref<StorageException>();
final boolean b = ((THashMap<Key, Value>)data).forEachEntry(new TObjectObjectProcedure<Key, Value>() {
@Override
public boolean execute(Key key, Value value) {
@@ -81,8 +80,6 @@ public abstract class MapDiffUpdateData<Key, Value> extends UpdateData<Key, Valu
iterateRemovedKeys(removedOrChangedKeys.keySet(), inputId, consumer);
}
private static final boolean DO_INFO_DUMP = ApplicationManager.getApplication().isInternal();
private void calcDiff() throws StorageException {
if (removedOrChangedKeys != null) return;
@@ -103,10 +100,10 @@ public abstract class MapDiffUpdateData<Key, Value> extends UpdateData<Key, Valu
if (!Comparing.equal(newValueForKey, e.getValue()) ||
newValueForKey == null && !newValue.containsKey(e.getKey())
) {
if (removedOrChangedKeys == null) removedOrChangedKeys = new THashMap<>();
if (removedOrChangedKeys == null) removedOrChangedKeys = new THashMap<Key, Value>();
removedOrChangedKeys.put(e.getKey(), e.getValue());
if (newValue.containsKey(e.getKey())) {
if (addedKeys == null) addedKeys = new THashMap<>();
if (addedKeys == null) addedKeys = new THashMap<Key, Value>();
addedKeys.put(e.getKey(), newValueForKey);
}
}
@@ -130,7 +127,7 @@ public abstract class MapDiffUpdateData<Key, Value> extends UpdateData<Key, Valu
}
for (Map.Entry<Key, Value> e : newValue.entrySet()) {
if (!currentValue.containsKey(e.getKey())) {
if (addedKeys == null) addedKeys = new THashMap<>();
if (addedKeys == null) addedKeys = new THashMap<Key, Value>();
addedKeys.put(e.getKey(), e.getValue());
}
}
@@ -145,7 +142,7 @@ public abstract class MapDiffUpdateData<Key, Value> extends UpdateData<Key, Valu
incrementalAdditions.addAndGet(addedKeys.size());
incrementalRemovals.addAndGet(removedOrChangedKeys.size());
if ((totalRequests & 0xFFF) == 0 && DO_INFO_DUMP) {
if ((totalRequests & 0xFFF) == 0 && DebugAssertions.DEBUG) {
Logger.getInstance(getClass()).info("Incremental index diff update:"+requests +
", removals:" + totalRemovals + "->" + incrementalRemovals +
", additions:" +totalAdditions + "->" +incrementalAdditions);
@@ -0,0 +1,271 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.indexing;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.Processor;
import com.intellij.util.containers.SLRUCache;
import com.intellij.util.io.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public abstract class MapIndexStorageBase<Key, Value> implements IndexStorage<Key, Value> {
private static final Logger LOG = Logger.getInstance(MapIndexStorageBase.class);
protected PersistentMap<Key, ValueContainer<Value>> myMap;
protected SLRUCache<Key, ChangeTrackingValueContainer<Value>> myCache;
protected final File myBaseStorageFile;
protected final KeyDescriptor<Key> myKeyDescriptor;
private final int myCacheSize;
protected final Lock l = new ReentrantLock();
private final DataExternalizer<Value> myDataExternalizer;
private final boolean myKeyIsUniqueForIndexedFile;
protected MapIndexStorageBase(@NotNull File storageFile,
@NotNull KeyDescriptor<Key> keyDescriptor,
@NotNull DataExternalizer<Value> valueExternalizer,
final int cacheSize,
boolean keyIsUniqueForIndexedFile) throws IOException {
myBaseStorageFile = storageFile;
myKeyDescriptor = keyDescriptor;
myCacheSize = cacheSize;
myDataExternalizer = valueExternalizer;
myKeyIsUniqueForIndexedFile = keyIsUniqueForIndexedFile;
}
protected void initMapAndCache() throws IOException {
final ValueContainerMap<Key, Value> map;
PersistentHashMapValueStorage.CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.set(
new PersistentHashMapValueStorage.ExceptionalIOCancellationCallback() {
@Override
public void checkCancellation() {
checkCanceled();
}
});
PersistentHashMapValueStorage.CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.set(Boolean.TRUE);
try {
map = new ValueContainerMap<Key, Value>(getStorageFile(), myKeyDescriptor, myDataExternalizer, myKeyIsUniqueForIndexedFile);
} finally {
PersistentHashMapValueStorage.CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.set(null);
PersistentHashMapValueStorage.CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.set(null);
}
myCache = new SLRUCache<Key, ChangeTrackingValueContainer<Value>>(myCacheSize, (int)(Math.ceil(myCacheSize * 0.25)) /* 25% from the main cache size*/) {
@Override
@NotNull
public ChangeTrackingValueContainer<Value> createValue(final Key key) {
return new ChangeTrackingValueContainer<Value>(new ChangeTrackingValueContainer.Initializer<Value>() {
@NotNull
@Override
public Object getLock() {
return map.getDataAccessLock();
}
@Nullable
@Override
public ValueContainer<Value> compute() {
ValueContainer<Value> value;
try {
value = map.get(key);
if (value == null) {
value = new ValueContainerImpl<Value>();
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
return value;
}
});
}
@Override
protected void onDropFromCache(final Key key, @NotNull final ChangeTrackingValueContainer<Value> valueContainer) {
if (valueContainer.isDirty()) {
try {
map.put(key, valueContainer);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
};
myMap = map;
}
@NotNull
private File getStorageFile() {
return new File(myBaseStorageFile.getPath() + ".storage");
}
@Override
public boolean processKeys(@NotNull Processor<Key> processor) throws StorageException {
l.lock();
try {
myCache.clear(); // this will ensure that all new keys are made into the map
return myMap.processKeys(processor);
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
return unwrapCauseAndRethrow(e);
}
finally {
l.unlock();
}
}
@Override
public void flush() {
l.lock();
try {
if (!myMap.isClosed()) {
myCache.clear();
if (myMap.isDirty()) myMap.force();
}
}
finally {
l.unlock();
}
}
@Override
public void close() throws StorageException {
try {
flush();
myMap.close();
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
unwrapCauseAndRethrow(e);
}
}
@Override
public void clear() throws StorageException{
try {
myMap.close();
}
catch (IOException e) {
LOG.error(e);
}
catch (RuntimeException e) {
LOG.error(e);
}
try {
IOUtil.deleteAllFilesStartingWith(getStorageFile());
initMapAndCache();
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
unwrapCauseAndRethrow(e);
}
}
@NotNull
@Override
public Collection<Key> getKeys() throws StorageException {
List<Key> keys = new ArrayList<Key>();
processKeys(new CancelableCollectProcessor<Key>(keys, new Runnable() {
@Override
public void run() {checkCanceled();}
}));
return keys;
}
@Override
@NotNull
public ChangeTrackingValueContainer<Value> read(final Key key) throws StorageException {
l.lock();
try {
return myCache.get(key);
}
catch (RuntimeException e) {
return unwrapCauseAndRethrow(e);
}
finally {
l.unlock();
}
}
@Override
public void addValue(final Key key, final int inputId, final Value value) throws StorageException {
try {
myMap.markDirty();
if (!myKeyIsUniqueForIndexedFile) {
read(key).addValue(inputId, value);
return;
}
ChangeTrackingValueContainer<Value> cached;
try {
l.lock();
cached = myCache.getIfCached(key);
} finally {
l.unlock();
}
if (cached != null) {
cached.addValue(inputId, value);
return;
}
// do not pollute the cache with keys unique to indexed file
ChangeTrackingValueContainer<Value> valueContainer = new ChangeTrackingValueContainer<Value>(null);
valueContainer.addValue(inputId, value);
myMap.put(key, valueContainer);
}
catch (IOException e) {
throw new StorageException(e);
}
}
@Override
public void removeAllValues(@NotNull Key key, int inputId) throws StorageException {
try {
myMap.markDirty();
// important: assuming the key exists in the index
read(key).removeAssociatedValue(inputId);
}
catch (IOException e) {
throw new StorageException(e);
}
}
protected static <T> T unwrapCauseAndRethrow(RuntimeException e) throws StorageException {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw new StorageException(cause);
}
if (cause instanceof StorageException) {
throw (StorageException)cause;
}
throw e;
}
}
@@ -0,0 +1,441 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.indexing;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.LowMemoryWatcher;
import com.intellij.openapi.util.NotNullComputable;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.util.Processor;
import com.intellij.util.io.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class MapReduceIndexBase<Key,Value, Input> implements AbstractUpdatableIndex<Key, Value, Input> {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.indexing.MapReduceIndex");
@Nullable protected final ID<Key, Value> myIndexId;
protected final DataIndexer<Key, Value, Input> myIndexer;
@NotNull protected final IndexStorage<Key, Value> myStorage;
protected final DataExternalizer<Value> myValueExternalizer;
protected final IndexExtension<Key, Value, Input> myExtension;
private final AtomicLong myModificationStamp = new AtomicLong();
@Nullable protected PersistentHashMap<Integer, Collection<Key>> myInputsIndex;
private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock();
private final LowMemoryWatcher myLowMemoryFlusher = LowMemoryWatcher.register(new Runnable() {
@Override
public void run() {
try {
if (myStorage instanceof MemoryIndexStorageBase) {
Lock writeLock = getWriteLock();
if (writeLock.tryLock()) {
try {
((MemoryIndexStorageBase<?, ?, ?>)myStorage).clearCaches();
} finally {
writeLock.unlock();
}
}
}
flush();
} catch (StorageException e) {
LOG.info(e);
requestRebuild(e);
}
}
});
protected MapReduceIndexBase(IndexExtension<Key, Value, Input> extension,
@NotNull IndexStorage<Key, Value> storage,
boolean createInputIndex) throws IOException {
myIndexId = extension.getName();
myExtension = extension;
myIndexer = extension.getIndexer();
myStorage = storage;
myValueExternalizer = extension.getValueExternalizer();
if (createInputIndex) {
myInputsIndex = createInputsIndex();
}
}
protected PersistentHashMap<Integer, Collection<Key>> createInputsIndex() throws IOException {
return null;
}
protected <K> DataExternalizer<Collection<K>> createInputsIndexExternalizer(IndexExtension<K, ?, ?> extension,
ID<K, ?> indexId,
KeyDescriptor<K> keyDescriptor) {
DataExternalizer<Collection<K>> externalizer;
externalizer = new InputIndexDataExternalizer<K>(keyDescriptor, indexId);
return externalizer;
}
@NotNull
public IndexStorage<Key, Value> getStorage() {
return myStorage;
}
@Override
public void clear() throws StorageException {
try {
getWriteLock().lock();
myStorage.clear();
if (myInputsIndex != null) {
cleanMapping(myInputsIndex);
myInputsIndex = createInputsIndex();
}
cleanMappings();
}
catch (StorageException e) {
LOG.error(e);
}
catch (IOException e) {
LOG.error(e);
}
finally {
getWriteLock().unlock();
}
}
protected void cleanMappings() throws IOException {
}
protected static void cleanMapping(@NotNull PersistentHashMap<?, ?> index) {
final File baseFile = index.getBaseFile();
try {
index.close();
}
catch (Throwable ignored) {
}
IOUtil.deleteAllFilesStartingWith(baseFile);
}
@Override
public void flush() throws StorageException{
try {
getReadLock().lock();
doForce(myInputsIndex);
doForce();
myStorage.flush();
}
catch (IOException e) {
throw new StorageException(e);
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof StorageException || cause instanceof IOException) {
throw new StorageException(cause);
}
else {
throw e;
}
}
finally {
getReadLock().unlock();
}
}
protected void doForce() {
}
protected static void doForce(PersistentHashMap<?, ?> inputsIndex) {
if (inputsIndex != null && inputsIndex.isDirty()) {
inputsIndex.force();
}
}
@Override
public void dispose() {
myLowMemoryFlusher.stop();
final Lock lock = getWriteLock();
try {
lock.lock();
try {
myStorage.close();
}
finally {
doClose(myInputsIndex);
doClose();
}
}
catch (StorageException e) {
LOG.error(e);
}
finally {
lock.unlock();
}
}
protected void doClose() {
}
protected static void doClose(PersistentHashMap<?, ?> index) {
if (index != null) {
try {
index.close();
}
catch (IOException e) {
LOG.error(e);
}
}
}
@NotNull
@Override
public final Lock getReadLock() {
return myLock.readLock();
}
@NotNull
@Override
public final Lock getWriteLock() {
return myLock.writeLock();
}
@Override
@NotNull
public ValueContainer<Value> getData(@NotNull final Key key) throws StorageException {
final Lock lock = getReadLock();
try {
lock.lock();
ValueContainerImpl.ourDebugIndexInfo.set(myIndexId);
return myStorage.read(key);
}
finally {
ValueContainerImpl.ourDebugIndexInfo.set(null);
lock.unlock();
}
}
@Override
public boolean processAllKeys(@NotNull Processor<Key> processor) throws StorageException {
final Lock lock = getReadLock();
try {
lock.lock();
return myStorage.processKeys(processor);
}
finally {
lock.unlock();
}
}
@NotNull
@Override
public Computable<Boolean> update(final int inputId, @Nullable Input content) {
Map<Key, Value> data = content != null ? myIndexer.map(content) : Collections.<Key, Value>emptyMap();
checkValuesHaveProperEqualsAndHashCode(data);
myStorage.checkCanceled();
// do not depend on content!
final UpdateData<Key, Value> updateData = buildUpdateData(data,
createOldKeysGetterByInputIndex(inputId),
inputId);
return new Computable<Boolean>() {
@Override
public Boolean compute() {
try {
updateWithMap(inputId, updateData);
}
catch (StorageException ex) {
LOG.info("Exception during updateWithMap:" + ex);
requestRebuild(ex);
return Boolean.FALSE;
}
catch (RuntimeException ex) {
if (getProcessCanceledExceptionClass().isInstance(ex)) {
LOG.info("Exception during updateWithMap:" + ex);
requestRebuild(ex);
return Boolean.FALSE;
} else {
throw ex;
}
}
return Boolean.TRUE;
}
};
}
@NotNull
protected NotNullComputable<Collection<Key>> createOldKeysGetterByInputIndex(final int inputId) {
return new NotNullComputable<Collection<Key>>() {
@NotNull
@Override
public Collection<Key> compute() {
try {
Collection<Key> oldKeys = MapReduceIndexBase.this.readInputKeys(inputId);
return oldKeys == null ? Collections.<Key>emptyList() : oldKeys;
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
};
}
protected UpdateData<Key, Value> buildUpdateData(Map<Key, Value> data, NotNullComputable<Collection<Key>> oldKeysGetter, int savedInputId) {
return new SimpleUpdateData(myIndexId, savedInputId, data, oldKeysGetter);
}
protected Collection<Key> readInputKeys(int inputId) throws IOException {
return myInputsIndex != null ? myInputsIndex.get(inputId) : null;
}
protected void saveInputKeys(int inputId, int savedInputId, Map<Key, Value> newData) throws IOException {
if (myInputsIndex != null) {
if (newData.size() > 0) {
myInputsIndex.put(inputId, newData.keySet());
}
else {
myInputsIndex.remove(inputId);
}
}
}
protected void checkValuesHaveProperEqualsAndHashCode(Map<Key, Value> data) {
if (DebugAssertions.DEBUG) {
for (Map.Entry<Key, Value> e : data.entrySet()) {
final Value value = e.getValue();
if (!(Comparing.equal(value, value) && (value == null || value.hashCode() == value.hashCode()))) {
LOG.error("Index " + myIndexId.toString() + " violates equals / hashCode contract for Value parameter");
}
if (myValueExternalizer != null) {
try {
final BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream();
DataOutputStream outputStream = new DataOutputStream(out);
myValueExternalizer.save(outputStream, value);
outputStream.close();
final Value deserializedValue =
myValueExternalizer.read(new DataInputStream(new UnsyncByteArrayInputStream(out.getInternalBuffer(), 0, out.size())));
if (!(Comparing.equal(value, deserializedValue) && (value == null || value.hashCode() == deserializedValue.hashCode()))) {
LOG.error("Index " + myIndexId.toString() + " deserialization violates equals / hashCode contract for Value parameter");
}
}
catch (IOException ex) {
LOG.error(ex);
}
}
}
}
}
public IndexExtension<Key, Value, Input> getExtension() {
return myExtension;
}
public long getModificationStamp() {
return myModificationStamp.get();
}
public class SimpleUpdateData extends UpdateData<Key, Value> {
private final int savedInputId;
private final @NotNull Map<Key, Value> newData;
protected final @NotNull NotNullComputable<Collection<Key>> oldKeysGetter;
public SimpleUpdateData(ID<Key,Value> indexId, int id, @NotNull Map<Key, Value> data, @NotNull NotNullComputable<Collection<Key>> getter) {
super(indexId);
savedInputId = id;
newData = data;
oldKeysGetter = getter;
}
public void iterateRemovedOrUpdatedKeys(int inputId, RemovedOrUpdatedKeyProcessor<Key> consumer) throws StorageException {
MapDiffUpdateData.iterateRemovedKeys(oldKeysGetter.compute(), inputId, consumer);
}
public void iterateAddedKeys(final int inputId, final AddedKeyProcessor<Key, Value> consumer) throws StorageException {
MapDiffUpdateData.iterateAddedKeyAndValues(inputId, consumer, newData);
}
@Override
public void save(int inputId) throws IOException {
saveInputKeys(inputId, savedInputId, newData);
}
public @NotNull Map<Key, Value> getNewData() {
return newData;
}
}
private final MapDiffUpdateData.RemovedOrUpdatedKeyProcessor<Key>
myRemoveStaleKeyOperation = new MapDiffUpdateData.RemovedOrUpdatedKeyProcessor<Key>() {
@Override
public void process(Key key, int inputId) throws StorageException {
myModificationStamp.incrementAndGet();
myStorage.removeAllValues(key, inputId);
}
};
private final MapDiffUpdateData.AddedKeyProcessor<Key, Value> myAddedKeyProcessor = new MapDiffUpdateData.AddedKeyProcessor<Key, Value>() {
@Override
public void process(Key key, Value value, int inputId) throws StorageException {
myModificationStamp.incrementAndGet();
myStorage.addValue(key, inputId, value);
}
};
protected void updateWithMap(final int inputId,
@NotNull UpdateData<Key, Value> updateData) throws StorageException {
getWriteLock().lock();
try {
try {
ValueContainerImpl.ourDebugIndexInfo.set(myIndexId);
updateData.iterateRemovedOrUpdatedKeys(inputId, myRemoveStaleKeyOperation);
updateData.iterateAddedKeys(inputId, myAddedKeyProcessor);
updateData.save(inputId);
}
catch (RuntimeException e) {
if (getProcessCanceledExceptionClass().isInstance(e)) {
throw e;
} else {
throw new StorageException(e);
}
}
catch (Throwable e) { // e.g. IOException, AssertionError
throw new StorageException(e);
}
finally {
ValueContainerImpl.ourDebugIndexInfo.set(null);
}
}
finally {
getWriteLock().unlock();
}
}
}
@@ -0,0 +1,223 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.indexing;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.*;
public abstract class MemoryIndexStorageBase<Key, Value, Storage extends IndexStorage<Key, Value>> implements IndexStorage<Key, Value> {
private final static Logger LOG = Logger.getInstance(MemoryIndexStorageBase.class);
private final Map<Key, ChangeTrackingValueContainer<Value>> myMap = new HashMap<Key, ChangeTrackingValueContainer<Value>>();
@NotNull
protected final Storage myBackendStorage;
private final List<BufferingStateListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final ID<?, ?> myIndexId;
private boolean myBufferingEnabled;
public interface BufferingStateListener {
void bufferingStateChanged(boolean newState);
void memoryStorageCleared();
}
public MemoryIndexStorageBase(@NotNull Storage backend, ID<?, ?> indexId) {
myBackendStorage = backend;
myIndexId = indexId;
}
@NotNull
public IndexStorage<Key, Value> getBackendStorage() {
return myBackendStorage;
}
public void addBufferingStateListener(@NotNull BufferingStateListener listener) {
myListeners.add(listener);
}
public void removeBufferingStateListener(@NotNull BufferingStateListener listener) {
myListeners.remove(listener);
}
public void setBufferingEnabled(boolean enabled) {
final boolean wasEnabled = myBufferingEnabled;
assert wasEnabled != enabled;
myBufferingEnabled = enabled;
for (BufferingStateListener listener : myListeners) {
listener.bufferingStateChanged(enabled);
}
}
public boolean isBufferingEnabled() {
return myBufferingEnabled;
}
public void clearMemoryMap() {
myMap.clear();
}
public void fireMemoryStorageCleared() {
for (BufferingStateListener listener : myListeners) {
listener.memoryStorageCleared();
}
}
void clearCaches() {
if (myMap.size() == 0) return;
if (DebugAssertions.DEBUG) {
String message = "Dropping caches for " + (myIndexId != null ? myIndexId:this) + ", number of items:" + myMap.size();
LOG.info(message);
}
for(ChangeTrackingValueContainer<Value> v:myMap.values()) {
v.dropMergedData();
}
}
@Override
public void close() throws StorageException {
myBackendStorage.close();
}
@Override
public void clear() throws StorageException {
clearMemoryMap();
myBackendStorage.clear();
}
@Override
public void flush() throws IOException {
myBackendStorage.flush();
}
@NotNull
@Override
public Collection<Key> getKeys() throws StorageException {
final Set<Key> keys = new HashSet<Key>();
processKeys(new CancelableCollectProcessor<Key>(keys, new Runnable() {
@Override
public void run() {
checkCanceled();
}
}));
return keys;
}
@Override
public boolean processKeys(@NotNull final Processor<Key> processor) throws StorageException {
final Processor<Key> uniqueResultProcessor = processCacheFirstProcessor(processor);
return uniqueResultProcessor != null && myBackendStorage.processKeys(uniqueResultProcessor);
}
@Nullable
protected Processor<Key> processCacheFirstProcessor(final Processor<Key> initialProcessor) {
if (myMap.isEmpty()) return initialProcessor;
final Set<Key> stopList = new HashSet<Key>();
Processor<Key> decoratingProcessor = new Processor<Key>() {
@Override
public boolean process(Key key) {
if (stopList.contains(key)) return true;
final UpdatableValueContainer<Value> container = myMap.get(key);
if (container != null && container.size() == 0) {
return true;
}
return initialProcessor.process(key);
}
};
for (Key key : myMap.keySet()) {
if (!decoratingProcessor.process(key)) {
return null;
}
stopList.add(key);
}
return decoratingProcessor;
}
@Override
public void addValue(final Key key, final int inputId, final Value value) throws StorageException {
if (myBufferingEnabled) {
getMemValueContainer(key).addValue(inputId, value);
return;
}
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.dropMergedData();
}
myBackendStorage.addValue(key, inputId, value);
}
@Override
public void removeAllValues(@NotNull Key key, int inputId) throws StorageException {
if (myBufferingEnabled) {
getMemValueContainer(key).removeAssociatedValue(inputId);
return;
}
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.dropMergedData();
}
myBackendStorage.removeAllValues(key, inputId);
}
private UpdatableValueContainer<Value> getMemValueContainer(final Key key) {
ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer == null) {
valueContainer = new ChangeTrackingValueContainer<Value>(new ChangeTrackingValueContainer.Initializer<Value>() {
@Override
public Object getLock() {
return this;
}
@Override
public ValueContainer<Value> compute() {
try {
return myBackendStorage.read(key);
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
});
myMap.put(key, valueContainer);
}
return valueContainer;
}
@Override
@NotNull
public ValueContainer<Value> read(final Key key) throws StorageException {
final ValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
return valueContainer;
}
return myBackendStorage.read(key);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -79,20 +79,20 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
return myInputIdMapping != null ? myInputIdMapping instanceof THashMap ? ((THashMap)myInputIdMapping).size(): 1 : 0;
}
static final ThreadLocal<ID> ourDebugIndexInfo = new ThreadLocal<>();
static final ThreadLocal<ID> ourDebugIndexInfo = new ThreadLocal<ID>();
@Override
public void removeAssociatedValue(int inputId) {
if (myInputIdMapping == null) return;
List<Object> fileSetObjects = null;
List<Value> valueObjects = null;
for (final ValueIterator<Value> valueIterator = getValueIterator(); valueIterator.hasNext();) {
for (final ValueContainer.ValueIterator<Value> valueIterator = getValueIterator(); valueIterator.hasNext();) {
final Value value = valueIterator.next();
if (valueIterator.getValueAssociationPredicate().contains(inputId)) {
if (fileSetObjects == null) {
fileSetObjects = new SmartList<>();
valueObjects = new SmartList<>();
fileSetObjects = new SmartList<Object>();
valueObjects = new SmartList<Value>();
}
else if (DebugAssertions.DEBUG) {
LOG.error("Expected only one value per-inputId for " + ourDebugIndexInfo.get(), String.valueOf(fileSetObjects.get(0)), String.valueOf(value));
@@ -263,9 +263,9 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
if (myInputIdMapping == null) {
return Collections.emptyList();
} else if (myInputIdMapping instanceof THashMap) {
return new ArrayList<>(((THashMap<Value, Object>)myInputIdMapping).keySet());
return new ArrayList<Value>(((THashMap<Value, Object>)myInputIdMapping).keySet());
} else {
return new SmartList<>((Value)myInputIdMapping);
return new SmartList<Value>((Value)myInputIdMapping);
}
}
@@ -366,11 +366,11 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
@NotNull
public ValueContainerImpl<Value> copy() {
ValueContainerImpl<Value> container = new ValueContainerImpl<>();
ValueContainerImpl<Value> container = new ValueContainerImpl<Value>();
if (myInputIdMapping instanceof THashMap) {
final THashMap<Value, Object> mapping = (THashMap<Value, Object>)myInputIdMapping;
final THashMap<Value, Object> newMapping = new THashMap<>(mapping.size());
final THashMap<Value, Object> newMapping = new THashMap<Value, Object>(mapping.size());
container.myInputIdMapping = newMapping;
mapping.forEachEntry(new TObjectObjectProcedure<Value, Object>() {
@@ -485,7 +485,7 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
final int inputId = -valueCount;
if (mapping == null && size() > NUMBER_OF_VALUES_THRESHOLD) { // avoid O(NumberOfValues)
mapping = new FileId2ValueMapping<>(this);
mapping = new FileId2ValueMapping<Value>(this);
}
boolean doCompact;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ class ValueContainerMap<Key, Value> extends PersistentHashMap<Key, ValueContaine
@NotNull DataExternalizer<Value> valueExternalizer,
boolean keyIsUniqueForIndexedFile
) throws IOException {
super(file, keyKeyDescriptor, new ValueContainerExternalizer<>(valueExternalizer));
super(file, keyKeyDescriptor, new ValueContainerExternalizer<Value>(valueExternalizer));
myValueExternalizer = valueExternalizer;
myKeyIsUniqueForIndexedFile = keyIsUniqueForIndexedFile;
}
@@ -48,7 +48,7 @@ class ValueContainerMap<Key, Value> extends PersistentHashMap<Key, ValueContaine
@Override
protected void doPut(Key key, ValueContainer<Value> container) throws IOException {
synchronized (myEnumerator) {
ChangeTrackingValueContainer<Value> valueContainer = (ChangeTrackingValueContainer<Value>)container;
final ChangeTrackingValueContainer<Value> valueContainer = (ChangeTrackingValueContainer<Value>)container;
// try to accumulate index value calculated for particular key to avoid fragmentation: usually keys are scattered across many files
// note that keys unique for indexed file have their value calculated at once (e.g. key is file id, index calculates something for particular
@@ -83,7 +83,7 @@ class ValueContainerMap<Key, Value> extends PersistentHashMap<Key, ValueContaine
@NotNull
@Override
public ValueContainerImpl<T> read(@NotNull final DataInput in) throws IOException {
final ValueContainerImpl<T> valueContainer = new ValueContainerImpl<>();
final ValueContainerImpl<T> valueContainer = new ValueContainerImpl<T>();
valueContainer.readFrom((DataInputStream)in, myValueExternalizer);
return valueContainer;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -144,7 +144,7 @@ public class VcsLogFullDetailsIndex<T> implements Disposable {
}
@Override
protected void requestRebuild(@Nullable Exception ex) {
public void requestRebuild(@Nullable Exception ex) {
myFatalErrorHandler.consume(this, ex != null ? ex : new Exception("Index rebuild requested"));
}
}