mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-26 19:06:24 +07:00
PCE left classesBeingProcessed dirty fixed
This commit is contained in:
+89
-57
@@ -30,6 +30,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Collection of elements of type V which is
|
||||
@@ -53,9 +54,9 @@ class LazyConcurrentCollection<T,V> implements Iterable<V> {
|
||||
// If more elements requested for this iterator, the processMoreSubclasses() is called which tries to populate 'subClasses' with more inheritors.
|
||||
private final HashSetQueue<T> subClasses; // guarded by lock
|
||||
private final Object lock = new Object(); // MUST NOT acquire read action inside this lock
|
||||
@NotNull private final Function<T, V> myConvertor;
|
||||
@NotNull private final Function<T, V> myAnchorToValueConvertor;
|
||||
@NotNull private final MoreElementsGenerator<T,V> myGenerator;
|
||||
@NotNull private final Predicate<? super V> myApplicableFilter;
|
||||
@NotNull private final Predicate<? super V> myApplicableForGenerationFilter;
|
||||
private final Semaphore currentlyProcessingClasses = new Semaphore();
|
||||
|
||||
private final HashSetQueue.PositionalIterator<T> candidatesToFindSubclassesIterator; // guarded by lock
|
||||
@@ -67,13 +68,13 @@ class LazyConcurrentCollection<T,V> implements Iterable<V> {
|
||||
|
||||
LazyConcurrentCollection(@NotNull T seedElement,
|
||||
@NotNull Function<T, V> convertor,
|
||||
@NotNull Predicate<? super V> applicableFilter,
|
||||
@NotNull Predicate<? super V> applicableForGenerationFilter,
|
||||
@NotNull MoreElementsGenerator<T, V> generator) {
|
||||
subClasses = new HashSetQueue<>();
|
||||
subClasses.add(seedElement);
|
||||
myConvertor = convertor;
|
||||
myAnchorToValueConvertor = convertor;
|
||||
myGenerator = generator;
|
||||
myApplicableFilter = applicableFilter;
|
||||
myApplicableForGenerationFilter = applicableForGenerationFilter;
|
||||
candidatesToFindSubclassesIterator = subClasses.iterator();
|
||||
}
|
||||
|
||||
@@ -111,55 +112,58 @@ class LazyConcurrentCollection<T,V> implements Iterable<V> {
|
||||
synchronized (lock) {
|
||||
next = subClassIterator.next();
|
||||
}
|
||||
return myConvertor.fun(next);
|
||||
return myAnchorToValueConvertor.fun(next);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Pair.NonNull<T,V> findNextClassInQueue(@NotNull HashSetQueue.PositionalIterator.IteratorPosition<T> position) {
|
||||
// find the first class which is fit (not anonymous and not final and retrievable from PsiAnchor) and not already processed (flag PROCESSING_SUBCLASSES_STATUS in class user data)
|
||||
// couldn't call iterator.next() until class is processed, so use position.peek()/position.next() which don't advance iterator
|
||||
while (position != null) {
|
||||
ProgressManager.checkCanceled();
|
||||
T anchor = position.peek();
|
||||
V value = myConvertor.fun(anchor);
|
||||
boolean isAccepted = value != null && myApplicableFilter.apply(value);
|
||||
|
||||
if (isAccepted && !classesProcessed.contains(anchor) && classesBeingProcessed.add(anchor)) {
|
||||
return Pair.createNonNull(anchor, value);
|
||||
}
|
||||
// the candidate is already being processed in the other thread, try the next one (not advancing iterator!)
|
||||
position = position.next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// polls 'subClasses' for more sub classes and call DirectClassInheritorsSearch for them
|
||||
private void processMoreSubclasses(@NotNull Iterator<T> subClassIterator) {
|
||||
// returns true if some classes were found
|
||||
private boolean processMoreSubclasses(@NotNull Iterator<T> subClassIterator) {
|
||||
while (true) {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
Pair.NonNull<T,V> pair =
|
||||
ApplicationManager.getApplication().runReadAction(new Computable<Pair.NonNull<T,V>>() {
|
||||
@Override
|
||||
public Pair.NonNull<T,V> compute() {
|
||||
synchronized (lock) {
|
||||
// Find the classes in subClasses collection to operate on
|
||||
// (without advancing the candidatesToFindSubclassesIterator iterator - it will be moved after the class successfully handled - to protect against PCE, INRE, etc)
|
||||
// The found class will be marked as being analyzed - placed in classesBeingProcessed collection
|
||||
HashSetQueue.PositionalIterator.IteratorPosition<T> startPosition = candidatesToFindSubclassesIterator.position().next();
|
||||
Pair.NonNull<T,V> pair = startPosition == null ? null : findNextClassInQueue(startPosition);
|
||||
if (pair != null) {
|
||||
currentlyProcessingClasses.down();
|
||||
}
|
||||
return pair;
|
||||
ApplicationManager.getApplication().runReadAction((Computable<Pair.NonNull<T,V>>)() -> {
|
||||
synchronized (lock) {
|
||||
// Find the classes in subClasses collection to operate on
|
||||
// (without advancing the candidatesToFindSubclassesIterator iterator - it will be moved after the class successfully handled - to protect against PCE, INRE, etc)
|
||||
// The found class will be marked as being analyzed - placed in classesBeingProcessed collection
|
||||
HashSetQueue.PositionalIterator.IteratorPosition<T> startPosition = candidatesToFindSubclassesIterator.position().next();
|
||||
Pair.NonNull<T,V> next = startPosition == null ? null : findNextClassInQueue(startPosition);
|
||||
if (next != null) {
|
||||
currentlyProcessingClasses.down();
|
||||
classesBeingProcessed.add(next.getFirst());
|
||||
}
|
||||
return next;
|
||||
}
|
||||
});
|
||||
if (pair == null) {
|
||||
// no candidates left in queue, exit
|
||||
// but first, wait for other threads to process their candidates
|
||||
break;
|
||||
synchronized (lock) {
|
||||
advanceIteratorOnSuccess(); // to skip unsuitable classes like final etc from the queue
|
||||
if (subClassIterator.hasNext()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean producedSomething = waitForOtherThreadsToFinishProcessing(subClassIterator);
|
||||
if (producedSomething) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// aaaaaaaa! Other threads were unable to produce anything. That can be because:
|
||||
// - the whole queue has been processed. => exit, return false
|
||||
// - the other thread has been interrupted. => check the queue again to pickup the work it dropped.
|
||||
synchronized (lock) {
|
||||
advanceIteratorOnSuccess(); // to skip unsuitable classes like final etc from the queue
|
||||
if (!candidatesToFindSubclassesIterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
continue; // check again
|
||||
}
|
||||
|
||||
V candidate = pair.getSecond();
|
||||
@@ -170,29 +174,33 @@ class LazyConcurrentCollection<T,V> implements Iterable<V> {
|
||||
subClasses.add(generatedElement);
|
||||
}
|
||||
});
|
||||
synchronized (lock) {
|
||||
classesProcessed.add(anchor);
|
||||
advanceIteratorOnSuccess();
|
||||
if (subClassIterator.hasNext()) {
|
||||
// we've added something to subClasses so we can return and the iterator can move forward at least once;
|
||||
// more elements will be added on the subsequent call to .next()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
currentlyProcessingClasses.up();
|
||||
}
|
||||
|
||||
synchronized (lock) {
|
||||
classesBeingProcessed.remove(anchor);
|
||||
classesProcessed.add(anchor);
|
||||
advanceIteratorOnSuccess();
|
||||
if (subClassIterator.hasNext()) {
|
||||
// we've added something to subClasses so we can return and the iterator can move forward at least once;
|
||||
// more elements will be added on the subsequent call to .next()
|
||||
return;
|
||||
synchronized (lock) {
|
||||
classesBeingProcessed.remove(anchor);
|
||||
currentlyProcessingClasses.up();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean waitForOtherThreadsToFinishProcessing(@NotNull final Iterator<T> subClassIterator) {
|
||||
// Found nothing, have to wait for other threads because:
|
||||
// The first thread comes and takes a class off the queue to search for inheritors,
|
||||
// the second thread comes and sees there is no classes in the queue.
|
||||
// The second thread should not return nothing, it should wait for the first thread to finish.
|
||||
//
|
||||
// Wait within managedBlock to signal FJP this thread is locked (to avoid thread starvation and deadlocks)
|
||||
AtomicBoolean hasNext = new AtomicBoolean();
|
||||
try {
|
||||
ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
|
||||
@Override
|
||||
@@ -204,7 +212,10 @@ class LazyConcurrentCollection<T,V> implements Iterable<V> {
|
||||
@Override
|
||||
public boolean isReleasable() {
|
||||
synchronized (lock) {
|
||||
return !currentlyProcessingClasses.isDown() || subClassIterator.hasNext();
|
||||
// other thread produced something or all of them reached the end of list
|
||||
boolean producedSomething = subClassIterator.hasNext();
|
||||
hasNext.set(producedSomething); // store the result to avoid locking again after exit
|
||||
return producedSomething || !candidatesToFindSubclassesIterator.hasNext() || classesBeingProcessed.isEmpty();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -212,20 +223,41 @@ class LazyConcurrentCollection<T,V> implements Iterable<V> {
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return hasNext.get();
|
||||
}
|
||||
|
||||
private void advanceIteratorOnSuccess() {
|
||||
HashSetQueue.PositionalIterator.IteratorPosition<T> position = candidatesToFindSubclassesIterator.position().next();
|
||||
// under lock
|
||||
private Pair.NonNull<T,V> findNextClassInQueue(@NotNull HashSetQueue.PositionalIterator.IteratorPosition<T> position) {
|
||||
// find the first class suitable for analyzing inheritors of (not anonymous and not final and retrievable from PsiAnchor) and not already processed or being processed (by other thread)
|
||||
// couldn't call iterator.next() until class is processed, so use position.peek()/position.next() which don't advance iterator
|
||||
while (position != null) {
|
||||
T next = position.peek();
|
||||
if (classesProcessed.contains(next)) {
|
||||
ProgressManager.checkCanceled();
|
||||
T anchor = position.peek();
|
||||
if (!classesProcessed.contains(anchor) && !classesBeingProcessed.contains(anchor)) {
|
||||
V value = myAnchorToValueConvertor.fun(anchor);
|
||||
boolean isAccepted = value != null && myApplicableForGenerationFilter.apply(value);
|
||||
if (isAccepted) {
|
||||
return Pair.createNonNull(anchor, value);
|
||||
}
|
||||
classesProcessed.add(anchor);
|
||||
}
|
||||
// the candidate is already being processed in the other thread, try the next one (not advancing iterator!)
|
||||
position = position.next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// under lock
|
||||
private void advanceIteratorOnSuccess() {
|
||||
while (candidatesToFindSubclassesIterator.hasNext()) {
|
||||
T next = candidatesToFindSubclassesIterator.position().next().peek();
|
||||
boolean removed = classesProcessed.remove(next);
|
||||
if (removed) {
|
||||
candidatesToFindSubclassesIterator.next();
|
||||
classesProcessed.remove(next);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
position = position.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user