diff --git a/platform/lang-impl/src/com/intellij/psi/impl/cache/CacheManager.java b/platform/lang-impl/src/com/intellij/psi/impl/cache/CacheManager.java index 5773600672f6..3c89dc5f91d7 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/cache/CacheManager.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/cache/CacheManager.java @@ -23,6 +23,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.IndexPattern; import com.intellij.psi.search.IndexPatternProvider; +import com.intellij.util.CommonProcessors; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; @@ -40,6 +41,12 @@ public interface CacheManager { boolean processFilesWithWord(@NotNull Processor processor,@NotNull String word, short occurenceMask, @NotNull GlobalSearchScope scope, final boolean caseSensitively); + // IMPORTANT!!! + // Do not call indices directly or indirectly from 'process' method, deadlocks are possible (IDEADEV-42137). + public void collectVirtualFilesWithWord(@NotNull final CommonProcessors.CollectProcessor fileProcessor, + @NotNull final String word, final short occurrenceMask, + @NotNull final GlobalSearchScope scope, final boolean caseSensitively); + /** * @return all VirtualFile's that contain todo-items under project roots */ diff --git a/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexCacheManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexCacheManagerImpl.java index 6c4fc593fedc..7389cdcf7852 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexCacheManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexCacheManagerImpl.java @@ -45,9 +45,7 @@ import com.intellij.util.indexing.FileBasedIndex; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @author Eugene Zhuravlev @@ -78,44 +76,52 @@ public class IndexCacheManagerImpl implements CacheManager{ return (scope.isSearchOutsideRootModel() || index.isInContent(virtualFile) || index.isInLibrarySource(virtualFile)) && !virtualFile.getFileType().isBinary(); } + // IMPORTANT!!! + // Since implementation of virtualFileProcessor.process() may call indices directly or indirectly, + // we cannot call it inside FileBasedIndex.processValues() method except in collecting form + // If we do, deadlocks are possible (IDEADEV-42137). Process the files without not holding indices' read lock. @Override - public boolean processFilesWithWord(@NotNull final Processor psiFileProcessor, @NotNull final String word, final short occurrenceMask, @NotNull final GlobalSearchScope scope, final boolean caseSensitively) { + public void collectVirtualFilesWithWord(@NotNull final CommonProcessors.CollectProcessor fileProcessor, + @NotNull final String word, final short occurrenceMask, + @NotNull final GlobalSearchScope scope, final boolean caseSensitively) { if (myProject.isDefault()) { - return true; + return; } - final Set vFiles = new THashSet(); - final GlobalSearchScope projectScope = GlobalSearchScope.allScope(myProject).union(scope); + try { ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { FileBasedIndex.getInstance().processValues(IdIndex.NAME, new IdIndexEntry(word, caseSensitively), null, new FileBasedIndex.ValueProcessor() { + final FileIndexFacade index = FileIndexFacade.getInstance(myProject); @Override public boolean process(final VirtualFile file, final Integer value) { ProgressManager.checkCanceled(); final int mask = value.intValue(); - if ((mask & occurrenceMask) != 0) { - vFiles.add(file); + if ((mask & occurrenceMask) != 0 && scope.contains(file) && shouldBeFound(scope, file, index)) { + if (!fileProcessor.process(file)) return false; } return true; } - }, projectScope); + }, GlobalSearchScope.allScope(myProject).union(scope)); } }); } catch (IndexNotReadyException e) { throw new ProcessCanceledException(); } + } + @Override + public boolean processFilesWithWord(@NotNull final Processor psiFileProcessor, @NotNull final String word, final short occurrenceMask, @NotNull final GlobalSearchScope scope, final boolean caseSensitively) { + final List vFiles = new ArrayList(5); + collectVirtualFilesWithWord(new CommonProcessors.CollectProcessor(vFiles), word, occurrenceMask, scope, caseSensitively); if (vFiles.isEmpty()) return true; - final FileIndexFacade index = FileIndexFacade.getInstance(myProject); - final Processor virtualFileProcessor = new ReadActionProcessor() { @Override public boolean processInReadAction(VirtualFile virtualFile) { - LOG.assertTrue(virtualFile.isValid()); - if (virtualFile.isValid() && scope.contains(virtualFile) && shouldBeFound(scope, virtualFile, index)) { + if (virtualFile.isValid()) { final PsiFile psiFile = myPsiManager.findFile(virtualFile); return psiFile == null || psiFileProcessor.process(psiFile); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index b88a70b51ef6..992419927e8a 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -21,6 +21,7 @@ import com.intellij.concurrency.JobUtil; import com.intellij.ide.todo.TodoIndexPatternProvider; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.ReadActionProcessor; import com.intellij.openapi.application.Result; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; @@ -346,13 +347,14 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { myManager.startBatchFilesProcessingMode(); try { final List result = new ArrayList(); - boolean success = processFilesWithText(scope, searchContext, caseSensitively, text, new Processor() { - @Override - public boolean process(PsiFile file) { - result.add(file.getViewProvider().getVirtualFile()); - return true; - } - }, progress); + boolean success = processFilesWithText( + scope, + searchContext, + caseSensitively, + text, + new CommonProcessors.CollectProcessor(result), + progress + ); LOG.assertTrue(success); return result; } @@ -365,7 +367,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final short searchContext, final boolean caseSensitively, @NotNull String text, - @NotNull final Processor processor, + @NotNull final Processor processor, @Nullable ProgressIndicator progress) { List words = StringUtil.getWordsIn(text); if (words.isEmpty()) return true; @@ -375,10 +377,12 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return o2.length() - o1.length(); } }); - final Set fileSet; + final Set fileSet; + CacheManager cacheManager = CacheManager.SERVICE.getInstance(myManager.getProject()); + if (words.size() > 1) { - fileSet = new THashSet(); - Set copy = new THashSet(); + fileSet = new THashSet(); + Set copy = new THashSet(); for (int i = 0; i < words.size() - 1; i++) { if (progress != null) { progress.checkCanceled(); @@ -387,11 +391,14 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { ProgressManager.checkCanceled(); } final String word = words.get(i); - CacheManager.SERVICE.getInstance(myManager.getProject()).processFilesWithWord(new CommonProcessors.CollectProcessor(copy), word, searchContext, scope, caseSensitively); - if (i == 0) { - fileSet.addAll(copy); - } - else { + final int finalI = i; + cacheManager.collectVirtualFilesWithWord(new CommonProcessors.CollectProcessor(i != 0 ? copy:fileSet) { + @Override + protected boolean accept(VirtualFile virtualFile) { + return finalI == 0 || fileSet.contains(virtualFile); + } + }, word, searchContext, scope, caseSensitively); + if (i != 0) { fileSet.retainAll(copy); } copy.clear(); @@ -402,15 +409,37 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { else { fileSet = null; } - return CacheManager.SERVICE.getInstance(myManager.getProject()).processFilesWithWord(new Processor() { - @Override - public boolean process(PsiFile psiFile) { - if (fileSet != null && !fileSet.contains(psiFile)) { + + final String lastWord = words.get(words.size() - 1); + if (processor instanceof CommonProcessors.CollectProcessor) { + final CommonProcessors.CollectProcessor collectProcessor = (CommonProcessors.CollectProcessor)processor; + cacheManager.collectVirtualFilesWithWord(new CommonProcessors.CollectProcessor(collectProcessor.getResults()) { + @Override + public boolean process(VirtualFile virtualFile) { + if (fileSet == null || fileSet.contains(virtualFile)) return collectProcessor.process(virtualFile); return true; } - return processor.process(psiFile); + }, lastWord, searchContext, scope, caseSensitively); + return true; + } else { + THashSet files = new THashSet(); + cacheManager.collectVirtualFilesWithWord(new CommonProcessors.CollectProcessor(files) { + @Override + protected boolean accept(VirtualFile virtualFile) { + return fileSet == null || fileSet.contains(virtualFile); + } + }, lastWord, searchContext, scope, caseSensitively); + ReadActionProcessor readActionProcessor = new ReadActionProcessor() { + @Override + public boolean processInReadAction(VirtualFile virtualFile) { + return processor.process(virtualFile); + } + }; + for(VirtualFile file:files) { + if (!readActionProcessor.process(file)) return false; } - }, words.get(words.size() - 1), searchContext, scope, caseSensitively); + return true; + } } @Override @@ -828,22 +857,21 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { @NotNull GlobalSearchScope scope, @Nullable final PsiFile fileToIgnoreOccurencesIn, @Nullable ProgressIndicator progress) { - final int[] count = {0}; - if (!processFilesWithText(scope, UsageSearchContext.ANY, true, name, new Processor() { + final AtomicInteger count = new AtomicInteger(); + if (!processFilesWithText(scope, UsageSearchContext.ANY, true, name, new CommonProcessors.CollectProcessor (Collections.emptyList()) { + private final VirtualFile fileToIgnoreOccurencesInVirtualFile = + fileToIgnoreOccurencesIn != null ? fileToIgnoreOccurencesIn.getVirtualFile():null; + @Override - public boolean process(PsiFile file) { - if (file == fileToIgnoreOccurencesIn) return true; - synchronized (count) { - count[0]++; - return count[0] <= 10; - } + public boolean process(VirtualFile file) { + if (file == fileToIgnoreOccurencesInVirtualFile) return true; + int value = count.incrementAndGet(); + return value < 10; } }, progress)) { return SearchCostResult.TOO_MANY_OCCURRENCES; } - synchronized (count) { - return count[0] == 0 ? SearchCostResult.ZERO_OCCURRENCES : SearchCostResult.FEW_OCCURRENCES; - } + return count.get() == 0 ? SearchCostResult.ZERO_OCCURRENCES : SearchCostResult.FEW_OCCURRENCES; } }