/* * 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.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiLock; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import java.util.Collection; import java.util.Set; abstract class UpdateTask<Type> { private final Semaphore myUpdateSemaphore = new Semaphore(); private final Set<Type> myItemsBeingIndexed = ContainerUtil.newConcurrentSet(); private static final boolean DEBUG = false; final boolean processAll(Collection<Type> itemsToProcess, Project project) { if (DEBUG) trace("enter processAll"); try { boolean hasMoreToProcess; boolean allItemsProcessed = true; do { hasMoreToProcess = false; if (DEBUG) trace("observing " + itemsToProcess.size()); // todo we can decrease itemsToProcess for (Type item : itemsToProcess) { myUpdateSemaphore.down(); try { if (DEBUG) trace("about to process"); boolean processed = process(item, project); if (DEBUG) trace(processed ? "processed " : "skipped"); if (!processed) { hasMoreToProcess = true; allItemsProcessed = false; } } finally { myUpdateSemaphore.up(); } ProgressManager.checkCanceled(); } while (!myUpdateSemaphore.waitFor(500)) { // may need to wait until another threads are done with indexing if (Thread.holdsLock(PsiLock.LOCK)) { break; // hack. Most probably that other indexing threads is waiting for PsiLock, which we're are holding. } } ProgressManager.checkCanceled(); if (DEBUG) if (hasMoreToProcess) trace("reiterating"); } while (hasMoreToProcess); return allItemsProcessed; } finally { if (DEBUG) trace("exits processAll"); } } private boolean process(Type item, Project project) { if (myItemsBeingIndexed.add(item)) { try { doProcess(item, project); return true; } finally { myItemsBeingIndexed.remove(item); } } return false; } abstract void doProcess(Type item, Project project); protected static void trace(String s) { System.out.println(Thread.currentThread() + " " + s); } }