boolean (@NotNull Project project, @NotNull List<? extends VirtualFile> files, @NotNull ProgressIndicator progress, @NotNull AtomicBoolean stopped, @NotNull Processor<? super VirtualFile> localProcessor) { ApplicationEx app = (ApplicationEx)ApplicationManager.getApplication(); if (!app.isDispatchThread()) { CoreProgressManager.assertUnderProgress(progress); } List<VirtualFile> processedFiles = Collections.synchronizedList(new ArrayList<>(files.size())); while (true) { ProgressManager.checkCanceled(); ProgressIndicator wrapper = new SensitiveProgressWrapper(progress); ApplicationListener listener = new ApplicationListener() { @Override public void beforeWriteActionStart(@NotNull Object action) { wrapper.cancel(); } }; processedFiles.clear(); Disposable disposable = Disposer.newDisposable(); app.addApplicationListener(listener, disposable); boolean processorCanceled = false; try { if (app.isWriteAccessAllowed() || app.isReadAccessAllowed() && app.isWriteActionPending()) { // no point in processing in separate threads - they are doomed to fail to obtain read action anyway // do not wrap in impatient reader because every read action inside would trigger AU.CRRAE processorCanceled = !ContainerUtil.process(files, localProcessor); if (processorCanceled) { stopped.set(true); } processedFiles.addAll(files); } else if (app.isWriteActionPending()) { // we don't have read action now so wait for write action to complete } else { AtomicBoolean someTaskFailed = new AtomicBoolean(); Processor<VirtualFile> processor = vfile -> { ProgressManager.checkCanceled(); // optimisation: avoid unnecessary processing if it's doomed to fail because some other task has failed already, // and bail out of fork/join task as soon as possible if (someTaskFailed.get()) { return false; } try { // wrap in unconditional impatient reader to bail early at write action start, // regardless of whether was called from highlighting (already impatient-wrapped) or Find Usages action app.executeByImpatientReader(() -> { if (localProcessor.process(vfile)) { processedFiles.add(vfile); } else { stopped.set(true); } }); } catch (ProcessCanceledException e) { someTaskFailed.set(true); throw e; } return !stopped.get(); }; // try to run parallel read actions but fail as soon as possible try { JobLauncher.getInstance().invokeConcurrentlyUnderProgress(files, wrapper, processor); processorCanceled = stopped.get(); } catch (ProcessCanceledException e) { // wrapper can interrupt us (means write action is about to start) or by genuine exception in progress progress.checkCanceled(); } } } finally { Disposer.dispose(disposable); } if (processorCanceled) { return false; } if (processedFiles.size() == files.size()) { break; } // we failed to run read action in job launcher thread // run read action in our thread instead to wait for a write action to complete and resume parallel processing DumbService.getInstance(project).runReadActionInSmartMode(EmptyRunnable.getInstance()); Set<VirtualFile> t = new HashSet<>(files); synchronized (processedFiles) { t.removeAll(processedFiles); } files = new ArrayList<>(t); } return true; }