mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
+24
-21
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user