report nested index access

This commit is contained in:
Maxim.Mossienko
2015-11-24 20:08:53 +01:00
parent 1e0b63c610
commit a76455626c
4 changed files with 130 additions and 2 deletions
@@ -14,7 +14,6 @@
* limitations under the License.
*/
package com.intellij.index
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.command.impl.CurrentEditorProvider
import com.intellij.openapi.command.impl.UndoManagerImpl
@@ -33,20 +32,27 @@ import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.impl.PsiManagerEx
import com.intellij.psi.impl.cache.impl.id.IdIndex
import com.intellij.psi.impl.cache.impl.id.IdIndexEntry
import com.intellij.psi.impl.file.impl.FileManagerImpl
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys
import com.intellij.psi.impl.source.PostprocessReformattingAspect
import com.intellij.psi.impl.source.PsiFileWithStubSupport
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchHelper
import com.intellij.psi.stubs.SerializedStubTree
import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.stubs.StubUpdatingIndex
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.SkipSlowTestLocally
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
import com.intellij.util.Processor
import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.MapIndexStorage
import com.intellij.util.indexing.StorageException
import com.intellij.util.io.*
import org.jetbrains.annotations.NotNull
/**
* @author Eugene Zhuravlev
* @since Dec 12, 2007
@@ -425,4 +431,69 @@ public class IndexTest extends JavaCodeInsightFixtureTestCase {
assertNotNull(stubTree)
assertEquals(stubTreeHash, stubTree.hashCode())
}
public void "test report using index from other index"() throws IOException {
def vfile = myFixture.addClass("class Foo { void bar() {} }").getContainingFile().getVirtualFile();
def scope = GlobalSearchScope.allScope(project)
def foundClass = [false];
def foundMethod = [false];
try {
StubIndex.instance.processElements(JavaStubIndexKeys.CLASS_SHORT_NAMES, "Foo", project, scope,
PsiClass.class,
new Processor<PsiClass>() {
@Override
boolean process(PsiClass aClass) {
foundClass[0] = true
StubIndex.instance.processElements(JavaStubIndexKeys.METHODS, "bar", project, scope,
PsiMethod.class,
new Processor<PsiMethod>() {
@Override
boolean process(PsiMethod method) {
foundMethod[0] = true;
return true;
}
});
return true;
}
});
} catch (e) {
if (!(e instanceof RuntimeException)) throw e;
}
assertTrue(foundClass[0])
assertTrue(!foundMethod[0])
def foundId = [false];
def foundStub = [false];
try {
FileBasedIndex.instance.
processValues(IdIndex.NAME, new IdIndexEntry("Foo", true), null, new FileBasedIndex.ValueProcessor<Integer>() {
@Override
boolean process(VirtualFile file, Integer value) {
foundId[0] = true
FileBasedIndex.instance.processValues(
StubUpdatingIndex.INDEX_ID,
vfile.id,
null,
new FileBasedIndex.ValueProcessor<SerializedStubTree>() {
@Override
boolean process(VirtualFile file2, SerializedStubTree value2) {
foundStub[0] = true
return true
}
},
scope
);
return true
}
}, scope)
} catch (e) {
if (!(e instanceof RuntimeException)) throw e;
}
assertTrue(foundId[0])
assertTrue(!foundStub[0])
}
}
@@ -73,6 +73,7 @@ public class StubIndexImpl extends StubIndex implements ApplicationComponent, Pe
private final TObjectIntHashMap<ID<?, ?>> myIndexIdToVersionMap = new TObjectIntHashMap<ID<?, ?>>();
private final StubProcessingHelper myStubProcessingHelper;
private final IndexAccessValidator myAccessValidator = new IndexAccessValidator();
private StubIndexState myPreviouslyRegistered;
@@ -244,14 +245,20 @@ public class StubIndexImpl extends StubIndex implements ApplicationComponent, Pe
final MyIndex<Key> index = (MyIndex<Key>)myIndices.get(indexKey);
try {
myAccessValidator.checkAccessingIndexDuringOtherIndexProcessing(indexKey);
try {
// disable up-to-date check to avoid locks on attempt to acquire index write lock while holding at the same time the readLock for this index
FileBasedIndexImpl.disableUpToDateCheckForCurrentThread();
index.getReadLock().lock();
myAccessValidator.startedProcessingActivityForIndex(indexKey);
return index.getData(key).forEach(action);
}
finally {
myAccessValidator.stoppedProcessingActivityForIndex(indexKey);
index.getReadLock().unlock();
FileBasedIndexImpl.enableUpToDateCheckForCurrentThread();
}
@@ -138,6 +138,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
private volatile int myFilesModCount;
private final AtomicInteger myUpdatingFiles = new AtomicInteger();
private final Set<Project> myProjectsBeingUpdated = ContainerUtil.newConcurrentSet();
private final IndexAccessValidator myAccessValidator = new IndexAccessValidator();
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private volatile boolean myInitialized;
// need this variable for memory barrier
@@ -269,6 +270,8 @@ public class FileBasedIndexImpl extends FileBasedIndex {
return true;
}
@Override
public void requestReindex(@NotNull final VirtualFile file) {
myChangedFilesCollector.invalidateIndices(file, true);
@@ -961,11 +964,15 @@ public class FileBasedIndexImpl extends FileBasedIndex {
//assert project != null : "GlobalSearchScope#getProject() should be not-null for all index queries";
ensureUpToDate(indexId, project, filter, restrictToFile);
myAccessValidator.checkAccessingIndexDuringOtherIndexProcessing(indexId);
try {
index.getReadLock().lock();
myAccessValidator.startedProcessingActivityForIndex(indexId);
return computable.convert(index);
}
finally {
myAccessValidator.stoppedProcessingActivityForIndex(indexId);
index.getReadLock().unlock();
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2015 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.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NotNull;
import java.text.MessageFormat;
/**
* Created by Maxim.Mossienko on 11/23/2015.
*/
public class IndexAccessValidator {
private final ThreadLocal<ID<?, ?>> ourAlreadyProcessingIndices = new ThreadLocal<ID<?, ?>>();
public void checkAccessingIndexDuringOtherIndexProcessing(@NotNull ID<?, ?> indexKey) {
final ID<?, ?> alreadyProcessingIndex = ourAlreadyProcessingIndices.get();
if (alreadyProcessingIndex != null && alreadyProcessingIndex != indexKey) {
final String message = MessageFormat.format("Accessing ''{0}'' during processing ''{1}''. Nested different indices processing may cause deadlock",
indexKey.toString(),
alreadyProcessingIndex.toString());
if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message);
Logger.getInstance(FileBasedIndexImpl.class).error(message); // RuntimeException to skip rebuild
}
}
public void startedProcessingActivityForIndex(ID<?,?> indexId) { ourAlreadyProcessingIndices.set(indexId); }
public void stoppedProcessingActivityForIndex(ID<?,?> indexId) { ourAlreadyProcessingIndices.set(null); }
}