void (@NotNull Iterator<T> subClassIterator) { while (true) { ProgressManager.checkCanceled(); Pair.NonNull<T,V> pair = ReadAction.compute(() -> { ProgressManager.checkCanceled(); 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 synchronized (lock) { advanceIteratorOnSuccess(); // to skip unsuitable classes like final etc from the queue if (subClassIterator.hasNext()) { return; } } boolean producedSomething = waitForOtherThreadsToFinishProcessing(subClassIterator); if (producedSomething) { return; } // 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 pick up the work it dropped. synchronized (lock) { advanceIteratorOnSuccess(); // to skip unsuitable classes like final etc from the queue if (!candidatesToFindSubclassesIterator.hasNext()) { return; } } continue; // check again } V candidate = pair.getSecond(); T anchor = pair.getFirst(); try { myGenerator.generateMoreElementsFor(candidate, generatedElement -> { ProgressManager.checkCanceled(); synchronized (lock) { 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; } } } finally { synchronized (lock) { classesBeingProcessed.remove(anchor); currentlyProcessingClasses.up(); } } } }