IDEA-74456 Throwable at com.intellij.psi.stubs.StubUpdatingIndex$MyIndex.b:

Fix: When merging changes in memory with data from disk, support "one value per input id for each key" invariant
This commit is contained in:
Eugene Zhuravlev
2011-12-05 19:25:10 +01:00
parent 02d8976809
commit 95ab7ef8d2
7 changed files with 52 additions and 62 deletions
@@ -66,12 +66,12 @@ class ChangeTrackingValueContainer<Value> extends UpdatableValueContainer<Value>
}
@Override
public void removeAllValues(int inputId) {
public void removeAssociatedValue(int inputId) {
if (myMerged != null) {
myMerged.removeAllValues(inputId);
myMerged.removeAssociatedValue(inputId);
}
myAdded.removeAllValues(inputId);
myRemoved.removeAllValues(inputId);
myAdded.removeAssociatedValue(inputId);
myRemoved.removeAssociatedValue(inputId);
myInvalidated.add(inputId);
}
@@ -103,11 +103,6 @@ class ChangeTrackingValueContainer<Value> extends UpdatableValueContainer<Value>
return getMergedData().toValueList();
}
@Override
public int[] getInputIds(final Value value) {
return getMergedData().getInputIds(value);
}
@Override
public boolean isAssociated(final Value value, final int inputId) {
return getMergedData().isAssociated(value, inputId);
@@ -117,6 +112,11 @@ class ChangeTrackingValueContainer<Value> extends UpdatableValueContainer<Value>
public IntIterator getInputIdsIterator(final Value value) {
return getMergedData().getInputIdsIterator(value);
}
public void dropMergedData() {
myMerged = null;
}
// need 'synchronized' to ensure atomic initialization of merged data
// because several threads that acquired read lock may simultaneously execute the method
private ValueContainer<Value> getMergedData() {
@@ -133,29 +133,32 @@ class ChangeTrackingValueContainer<Value> extends UpdatableValueContainer<Value>
final ValueContainer<Value> fromDisk = myInitializer.compute();
final ContainerAction<Value> addAction = new ContainerAction<Value>() {
fromDisk.forEach(new ContainerAction<Value>() {
@Override
public void perform(final int id, final Value value) {
newMerged.addValue(id, value);
}
};
final ContainerAction<Value> removeAction = new ContainerAction<Value>() {
});
myInvalidated.forEach(new TIntProcedure() {
@Override
public boolean execute(int inputId) {
newMerged.removeAssociatedValue(inputId);
return true;
}
});
myRemoved.forEach(new ContainerAction<Value>() {
@Override
public void perform(final int id, final Value value) {
newMerged.removeValue(id, value);
}
};
fromDisk.forEach(addAction);
myInvalidated.forEach(new TIntProcedure() {
});
myAdded.forEach(new ContainerAction<Value>() {
@Override
public boolean execute(int inputId) {
newMerged.removeAllValues(inputId);
return true;
public void perform(final int id, final Value value) {
newMerged.removeAssociatedValue(id); // enforcing "one-value-per-file for particular key" invariant
newMerged.addValue(id, value);
}
});
myRemoved.forEach(removeAction);
myAdded.forEach(addAction);
setNeedsCompacting(fromDisk.needsCompacting());
myMerged = newMerged;
@@ -248,7 +248,7 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
try {
myMap.markDirty();
// important: assuming the key exists in the index
read(key).removeAllValues(inputId);
read(key).removeAssociatedValue(inputId);
}
catch (IOException e) {
throw new StorageException(e);
@@ -32,7 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Date: Dec 10, 2007
*/
public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value> {
private final Map<Key, UpdatableValueContainer<Value>> myMap = new HashMap<Key,UpdatableValueContainer<Value>>();
private final Map<Key, ChangeTrackingValueContainer<Value>> myMap = new HashMap<Key,ChangeTrackingValueContainer<Value>>();
private final IndexStorage<Key, Value> myBackendStorage;
private final List<BufferingStateListener> myListeners = ContainerUtil.createEmptyCOWList();
private final AtomicBoolean myBufferingEnabled = new AtomicBoolean(false);
@@ -114,13 +114,17 @@ public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value>
if (stopList.contains(key)) return true;
final UpdatableValueContainer<Value> container = myMap.get(key);
if (container != null && container.size() == 0) return true;
if (container != null && container.size() == 0) {
return true;
}
return processor.process(key);
}
};
for (Key key : myMap.keySet()) {
if (!decoratingProcessor.process(key)) return false;
if (!decoratingProcessor.process(key)) {
return false;
}
stopList.add(key);
}
return myBackendStorage.processKeys(decoratingProcessor);
@@ -132,9 +136,9 @@ public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value>
getMemValueContainer(key).addValue(inputId, value);
return;
}
final UpdatableValueContainer<Value> valueContainer = myMap.get(key);
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.addValue(inputId, value);
valueContainer.dropMergedData();
}
myBackendStorage.addValue(key, inputId, value);
@@ -146,9 +150,9 @@ public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value>
getMemValueContainer(key).removeValue(inputId, value);
return;
}
final UpdatableValueContainer<Value> valueContainer = myMap.get(key);
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.removeValue(inputId, value);
valueContainer.dropMergedData();
}
myBackendStorage.removeValue(key, inputId, value);
}
@@ -156,19 +160,19 @@ public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value>
@Override
public void removeAllValues(Key key, int inputId) throws StorageException {
if (myBufferingEnabled.get()) {
getMemValueContainer(key).removeAllValues(inputId);
getMemValueContainer(key).removeAssociatedValue(inputId);
return;
}
final UpdatableValueContainer<Value> valueContainer = myMap.get(key);
final ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer != null) {
valueContainer.removeAllValues(inputId);
valueContainer.dropMergedData();
}
myBackendStorage.removeAllValues(key, inputId);
}
private UpdatableValueContainer<Value> getMemValueContainer(final Key key) {
UpdatableValueContainer<Value> valueContainer = myMap.get(key);
ChangeTrackingValueContainer<Value> valueContainer = myMap.get(key);
if (valueContainer == null) {
valueContainer = new ChangeTrackingValueContainer<Value>(new ChangeTrackingValueContainer.Initializer<Value>() {
@Override
@@ -26,5 +26,5 @@ public abstract class UpdatableValueContainer<T> extends ValueContainer<T>{
public abstract boolean removeValue(int inputId, T value);
public abstract void removeAllValues(int inputId);
public abstract void removeAssociatedValue(int inputId);
}
@@ -38,8 +38,6 @@ public abstract class ValueContainer<Value> {
public abstract Iterator<Value> getValueIterator();
public abstract int[] getInputIds(Value value);
public abstract List<Value> toValueList();
public abstract int size();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2011 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,7 +16,6 @@
package com.intellij.util.indexing;
import com.intellij.util.ArrayUtil;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntIterator;
@@ -32,7 +31,7 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
public ValueContainerImpl() {
myInputIdMapping = new HashMap<Value, Object>(16, 0.98f);
}
@Override
public void addValue(int inputId, Value value) {
final Object input = myInputIdMapping.get(value);
@@ -60,16 +59,18 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
}
@Override
public void removeAllValues(int inputId) {
final List<Value> toRemove= new ArrayList<Value>();
public void removeAssociatedValue(int inputId) {
Value toRemove = null;
for (final Iterator<Value> valueIterator = getValueIterator(); valueIterator.hasNext();) {
final Value value = valueIterator.next();
if (isAssociated(value, inputId)) {
toRemove.add(value);
assert toRemove == null;
toRemove = value;
}
}
for (Value value : toRemove) {
removeValue(inputId, value);
if (toRemove != null) {
removeValue(inputId, toRemove);
}
}
@@ -111,22 +112,6 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
return new ArrayList<Value>(myInputIdMapping.keySet());
}
@Override
public int[] getInputIds(final Value value) {
final Object input = myInputIdMapping.get(value);
final int[] idSet;
if (input instanceof TIntHashSet) {
idSet = ((TIntHashSet)input).toArray();
}
else if (input instanceof Integer ){
idSet = new int[] {((Integer)input).intValue()};
}
else {
idSet = ArrayUtil.EMPTY_INT_ARRAY;
}
return idSet;
}
@Override
public boolean isAssociated(final Value value, final int inputId) {
final Object input = myInputIdMapping.get(value);
@@ -237,7 +222,7 @@ class ValueContainerImpl<Value> extends UpdatableValueContainer<Value> implement
return mySize;
}
}
private HashMap<Value, Object> mapCopy(final HashMap<Value, Object> map) {
if (map == null) {
return null;
@@ -116,7 +116,7 @@ class ValueContainerMap<Key, Value> extends PersistentHashMap<Key, ValueContaine
while (stream.available() > 0) {
final int valueCount = DataInputOutputUtil.readSINT(in);
if (valueCount < 0) {
valueContainer.removeAllValues(-valueCount);
valueContainer.removeAssociatedValue(-valueCount);
valueContainer.setNeedsCompacting(true);
}
else {