From 95ab7ef8d2206833f8a6b2a888fe12332e46fc6c Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 5 Dec 2011 19:23:08 +0100 Subject: [PATCH] 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 --- .../ChangeTrackingValueContainer.java | 45 ++++++++++--------- .../util/indexing/MapIndexStorage.java | 2 +- .../util/indexing/MemoryIndexStorage.java | 26 ++++++----- .../indexing/UpdatableValueContainer.java | 2 +- .../util/indexing/ValueContainer.java | 2 - .../util/indexing/ValueContainerImpl.java | 35 +++++---------- .../util/indexing/ValueContainerMap.java | 2 +- 7 files changed, 52 insertions(+), 62 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java b/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java index c9f257585457..9d38a9e0607f 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java @@ -66,12 +66,12 @@ class ChangeTrackingValueContainer extends UpdatableValueContainer } @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 extends UpdatableValueContainer 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 extends UpdatableValueContainer 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 getMergedData() { @@ -133,29 +133,32 @@ class ChangeTrackingValueContainer extends UpdatableValueContainer final ValueContainer fromDisk = myInitializer.compute(); - final ContainerAction addAction = new ContainerAction() { + fromDisk.forEach(new ContainerAction() { @Override public void perform(final int id, final Value value) { newMerged.addValue(id, value); } - }; - final ContainerAction removeAction = new ContainerAction() { + }); + myInvalidated.forEach(new TIntProcedure() { + @Override + public boolean execute(int inputId) { + newMerged.removeAssociatedValue(inputId); + return true; + } + }); + myRemoved.forEach(new ContainerAction() { @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() { @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; diff --git a/platform/lang-impl/src/com/intellij/util/indexing/MapIndexStorage.java b/platform/lang-impl/src/com/intellij/util/indexing/MapIndexStorage.java index f1c3a1aad070..f8012d9a7628 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/MapIndexStorage.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/MapIndexStorage.java @@ -248,7 +248,7 @@ public final class MapIndexStorage implements IndexStorage implements IndexStorage { - private final Map> myMap = new HashMap>(); + private final Map> myMap = new HashMap>(); private final IndexStorage myBackendStorage; private final List myListeners = ContainerUtil.createEmptyCOWList(); private final AtomicBoolean myBufferingEnabled = new AtomicBoolean(false); @@ -114,13 +114,17 @@ public class MemoryIndexStorage implements IndexStorage if (stopList.contains(key)) return true; final UpdatableValueContainer 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 implements IndexStorage getMemValueContainer(key).addValue(inputId, value); return; } - final UpdatableValueContainer valueContainer = myMap.get(key); + final ChangeTrackingValueContainer 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 implements IndexStorage getMemValueContainer(key).removeValue(inputId, value); return; } - final UpdatableValueContainer valueContainer = myMap.get(key); + final ChangeTrackingValueContainer 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 implements IndexStorage @Override public void removeAllValues(Key key, int inputId) throws StorageException { if (myBufferingEnabled.get()) { - getMemValueContainer(key).removeAllValues(inputId); + getMemValueContainer(key).removeAssociatedValue(inputId); return; } - final UpdatableValueContainer valueContainer = myMap.get(key); + final ChangeTrackingValueContainer valueContainer = myMap.get(key); if (valueContainer != null) { - valueContainer.removeAllValues(inputId); + valueContainer.dropMergedData(); } myBackendStorage.removeAllValues(key, inputId); } private UpdatableValueContainer getMemValueContainer(final Key key) { - UpdatableValueContainer valueContainer = myMap.get(key); + ChangeTrackingValueContainer valueContainer = myMap.get(key); if (valueContainer == null) { valueContainer = new ChangeTrackingValueContainer(new ChangeTrackingValueContainer.Initializer() { @Override diff --git a/platform/lang-impl/src/com/intellij/util/indexing/UpdatableValueContainer.java b/platform/lang-impl/src/com/intellij/util/indexing/UpdatableValueContainer.java index 2aa5b3658934..ea2da7b3d218 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/UpdatableValueContainer.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/UpdatableValueContainer.java @@ -26,5 +26,5 @@ public abstract class UpdatableValueContainer extends ValueContainer{ public abstract boolean removeValue(int inputId, T value); - public abstract void removeAllValues(int inputId); + public abstract void removeAssociatedValue(int inputId); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainer.java b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainer.java index fc5e202824c9..bdfdc4b4e0bb 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainer.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainer.java @@ -38,8 +38,6 @@ public abstract class ValueContainer { public abstract Iterator getValueIterator(); - public abstract int[] getInputIds(Value value); - public abstract List toValueList(); public abstract int size(); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java index 92df9262bd50..5cacf273efe6 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java @@ -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 extends UpdatableValueContainer implement public ValueContainerImpl() { myInputIdMapping = new HashMap(16, 0.98f); } - + @Override public void addValue(int inputId, Value value) { final Object input = myInputIdMapping.get(value); @@ -60,16 +59,18 @@ class ValueContainerImpl extends UpdatableValueContainer implement } @Override - public void removeAllValues(int inputId) { - final List toRemove= new ArrayList(); + public void removeAssociatedValue(int inputId) { + Value toRemove = null; for (final Iterator 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 extends UpdatableValueContainer implement return new ArrayList(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 extends UpdatableValueContainer implement return mySize; } } - + private HashMap mapCopy(final HashMap map) { if (map == null) { return null; diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java index 7ef2e06c1257..810a22d85227 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java @@ -116,7 +116,7 @@ class ValueContainerMap extends PersistentHashMap 0) { final int valueCount = DataInputOutputUtil.readSINT(in); if (valueCount < 0) { - valueContainer.removeAllValues(-valueCount); + valueContainer.removeAssociatedValue(-valueCount); valueContainer.setNeedsCompacting(true); } else {