deadlock fix

This commit is contained in:
Eugene Zhuravlev
2009-09-04 20:16:27 +04:00
parent eb965a3305
commit 9a7d6d16e6
3 changed files with 63 additions and 73 deletions
@@ -36,6 +36,7 @@ import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
@@ -374,10 +375,14 @@ public class StubIndexImpl extends StubIndex implements ApplicationComponent, Pe
return Collections.<StubIndexKey>unmodifiableCollection(myIndices.keySet());
}
public <K> void updateIndex(StubIndexKey key, int fileId, Map<K, TIntArrayList> oldValues, Map<K, TIntArrayList> newValues) {
public <K> void updateIndex(StubIndexKey key, int fileId, final Map<K, TIntArrayList> oldValues, Map<K, TIntArrayList> newValues) {
try {
MyIndex<K> index = (MyIndex<K>)myIndices.get(key);
index.updateWithMap(fileId, newValues, oldValues.keySet());
final MyIndex<K> index = (MyIndex<K>)myIndices.get(key);
index.updateWithMap(fileId, newValues, new Callable<Collection<K>>() {
public Collection<K> call() throws Exception {
return oldValues.keySet();
}
});
}
catch (StorageException e) {
LOG.info(e);
@@ -390,14 +395,8 @@ public class StubIndexImpl extends StubIndex implements ApplicationComponent, Pe
super(null, null, storage);
}
public void updateWithMap(final int inputId, final Map<K, TIntArrayList> newData, Collection<K> oldKeys) throws StorageException {
getWriteLock().lock();
try {
super.updateWithMap(inputId, newData, oldKeys);
}
finally {
getWriteLock().unlock();
}
public void updateWithMap(final int inputId, final Map<K, TIntArrayList> newData, Callable<Collection<K>> oldKeysGetter) throws StorageException {
super.updateWithMap(inputId, newData, oldKeysGetter);
}
}
@@ -32,7 +32,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.Callable;
public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtension<Integer, SerializedStubTree, FileContent> {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.StubUpdatingIndex");
@@ -256,8 +256,7 @@ public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtensi
}
}
private static void updateStubIndices(final Collection<StubIndexKey> indexKeys, final int inputId, final Map<StubIndexKey, Map<Object, TIntArrayList>> oldStubTree,
final Map<StubIndexKey, Map<Object, TIntArrayList>> newStubTree) {
private static void updateStubIndices(final Collection<StubIndexKey> indexKeys, final int inputId, final Map<StubIndexKey, Map<Object, TIntArrayList>> oldStubTree, final Map<StubIndexKey, Map<Object, TIntArrayList>> newStubTree) {
final StubIndexImpl stubIndex = (StubIndexImpl)StubIndex.getInstance();
for (StubIndexKey key : indexKeys) {
final Map<Object, TIntArrayList> oldMap = oldStubTree.get(key);
@@ -290,26 +289,29 @@ public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtensi
}
}
protected void updateWithMap(final int inputId, final Map<Integer, SerializedStubTree> newData, Collection<Integer> oldKeys)
protected void updateWithMap(final int inputId, final Map<Integer, SerializedStubTree> newData, Callable<Collection<Integer>> oldKeysGetter)
throws StorageException {
checkNameStorage();
Map<Integer, SerializedStubTree> oldData = readOldData(inputId);
final Map<StubIndexKey, Map<Object, TIntArrayList>> oldStubTree = getStubTree(oldData);
final Map<StubIndexKey, Map<Object, TIntArrayList>> newStubTree = getStubTree(newData);
final Collection<StubIndexKey> affectedIndices = getAffectedIndices(oldStubTree, newStubTree);
final StubIndexImpl stubIndex = (StubIndexImpl)StubIndex.getInstance();
final Collection<StubIndexKey> allStubIndices = stubIndex.getAllStubIndexKeys();
try {
// first write-lock affected stub indices to avoid deadlocks
for (StubIndexKey key : affectedIndices) {
for (StubIndexKey key : allStubIndices) {
stubIndex.getWriteLock(key).lock();
}
try {
getWriteLock().lock();
super.updateWithMap(inputId, newData, oldKeys);
updateStubIndices(affectedIndices, inputId, oldStubTree, newStubTree);
final Map<Integer, SerializedStubTree> oldData = readOldData(inputId);
final Map<StubIndexKey, Map<Object, TIntArrayList>> oldStubTree = getStubTree(oldData);
super.updateWithMap(inputId, newData, oldKeysGetter);
updateStubIndices(getAffectedIndices(oldStubTree, newStubTree), inputId, oldStubTree, newStubTree);
}
finally {
getWriteLock().unlock();
@@ -317,7 +319,7 @@ public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtensi
}
finally {
for (StubIndexKey key : affectedIndices) {
for (StubIndexKey key : allStubIndices) {
stubIndex.getWriteLock(key).unlock();
}
}
@@ -344,24 +346,17 @@ public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtensi
return stubTree;
}
/*MUST be called from under the WriteLock*/
private Map<Integer, SerializedStubTree> readOldData(final int key) throws StorageException {
final Map<Integer, SerializedStubTree> result = new HashMap<Integer, SerializedStubTree>();
final Lock lock = getReadLock();
try {
lock.lock();
final ValueContainer<SerializedStubTree> valueContainer = getData(key);
if (valueContainer.size() != 1) {
LOG.assertTrue(valueContainer.size() == 0);
return result;
}
result.put(key, valueContainer.getValueIterator().next());
}
finally {
lock.unlock();
final ValueContainer<SerializedStubTree> valueContainer = myStorage.read(key);
if (valueContainer.size() != 1) {
LOG.assertTrue(valueContainer.size() == 0);
return result;
}
result.put(key, valueContainer.getValueIterator().next());
return result;
}
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -22,7 +23,7 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
private static final Logger LOG = Logger.getInstance("#com.intellij.util.indexing.MapReduceIndex");
private final ID<Key, Value> myIndexId;
private final DataIndexer<Key, Value, Input> myIndexer;
private final IndexStorage<Key, Value> myStorage;
protected final IndexStorage<Key, Value> myStorage;
private PersistentHashMap<Integer, Collection<Key>> myInputsIndex;
private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock();
@@ -183,56 +184,51 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
}
public final void update(final int inputId, @Nullable Input content) throws StorageException {
//final Map<Key, Value> oldData = mapOld(oldContent);
assert myInputsIndex != null;
final Map<Key, Value> data = mapNew(content);
Collection<Key> oldKeys;
try {
oldKeys = myInputsIndex.get(inputId);
}
catch (IOException e) {
throw new StorageException(e);
}
if (oldKeys == null) {
oldKeys = Collections.emptyList();
}
final Map<Key, Value> data = content != null ? myIndexer.map(content) : Collections.<Key, Value>emptyMap();
updateWithMap(inputId, data, new Callable<Collection<Key>>() {
public Collection<Key> call() throws Exception {
final Collection<Key> oldKeys = myInputsIndex.get(inputId);
return oldKeys == null? Collections.<Key>emptyList() : oldKeys;
}
});
}
protected void updateWithMap(final int inputId, final Map<Key, Value> newData, Callable<Collection<Key>> oldKeysGetter) throws StorageException {
getWriteLock().lock();
try {
// remove outdated values
updateWithMap(inputId, data, oldKeys);
try {
final Set<Key> newKeys = data.keySet();
if (newKeys.size() > 0) {
myInputsIndex.put(inputId, newKeys);
}
else {
myInputsIndex.remove(inputId);
for (Key key : oldKeysGetter.call()) {
myStorage.removeAllValues(key, inputId);
}
}
catch (IOException e) {
catch (Exception e) {
throw new StorageException(e);
}
// add new values
for (Map.Entry<Key, Value> entry : newData.entrySet()) {
myStorage.addValue(entry.getKey(), inputId, entry.getValue());
}
if (myInputsIndex != null) {
try {
final Set<Key> newKeys = newData.keySet();
if (newKeys.size() > 0) {
myInputsIndex.put(inputId, newKeys);
}
else {
myInputsIndex.remove(inputId);
}
}
catch (IOException e) {
throw new StorageException(e);
}
}
}
finally {
getWriteLock().unlock();
}
}
protected Map<Key, Value> mapNew(final Input content) throws StorageException {
return content != null ? myIndexer.map(content) : Collections.<Key, Value>emptyMap();
}
protected void updateWithMap(final int inputId, final Map<Key, Value> newData, Collection<Key> oldKeys) throws StorageException {
for (Key key : oldKeys) {
myStorage.removeAllValues(key, inputId);
}
// add new values
for (Key key : newData.keySet()) {
final Value newValue = newData.get(key);
myStorage.addValue(key, inputId, newValue);
}
scheduleFlush();
}