From a76455626c5d3fd2015ef4320a71cafe9b09c7ab Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Tue, 24 Nov 2015 20:01:37 +0100 Subject: [PATCH] report nested index access --- .../com/intellij/index/IndexTest.groovy | 75 ++++++++++++++++++- .../com/intellij/psi/stubs/StubIndexImpl.java | 7 ++ .../util/indexing/FileBasedIndexImpl.java | 7 ++ .../util/indexing/IndexAccessValidator.java | 43 +++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/util/indexing/IndexAccessValidator.java diff --git a/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy b/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy index c09e5916dc25..ea5282c781e4 100644 --- a/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy +++ b/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy @@ -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() { + @Override + boolean process(PsiClass aClass) { + foundClass[0] = true + StubIndex.instance.processElements(JavaStubIndexKeys.METHODS, "bar", project, scope, + PsiMethod.class, + new Processor() { + @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() { + @Override + boolean process(VirtualFile file, Integer value) { + foundId[0] = true + FileBasedIndex.instance.processValues( + StubUpdatingIndex.INDEX_ID, + vfile.id, + null, + new FileBasedIndex.ValueProcessor() { + @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]) + } } diff --git a/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java b/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java index 96d3a462cdc3..9a10599e9e7a 100644 --- a/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java @@ -73,6 +73,7 @@ public class StubIndexImpl extends StubIndex implements ApplicationComponent, Pe private final TObjectIntHashMap> myIndexIdToVersionMap = new TObjectIntHashMap>(); 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 index = (MyIndex)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(); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index adc1ee3d9d03..3acbe2ad89f0 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -138,6 +138,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { private volatile int myFilesModCount; private final AtomicInteger myUpdatingFiles = new AtomicInteger(); private final Set 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(); } } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/IndexAccessValidator.java b/platform/lang-impl/src/com/intellij/util/indexing/IndexAccessValidator.java new file mode 100644 index 000000000000..c82c3fa83ea6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/indexing/IndexAccessValidator.java @@ -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> ourAlreadyProcessingIndices = new ThreadLocal>(); + + 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); } +}