private <V, E extends Throwable> V registerIndicatorAndRun(@NotNull ProgressIndicator indicator,
                                                             @NotNull Thread currentThread,
                                                             ProgressIndicator oldIndicator,
                                                             @NotNull ThrowableComputable<V, E> process) throws E {
    List<Set<Thread>> threadsUnderThisIndicator = new ArrayList<>();
    synchronized (threadsUnderIndicator) {
      boolean oneOfTheIndicatorsIsCanceled = false;

      for (ProgressIndicator thisIndicator = indicator;
           thisIndicator != null;
           thisIndicator = thisIndicator instanceof WrappedProgressIndicator
                           ? ((WrappedProgressIndicator)thisIndicator).getOriginalProgressIndicator()
                           : null) {
        Set<Thread> underIndicator = threadsUnderIndicator.computeIfAbsent(thisIndicator, __ -> new HashSet<>());
        boolean alreadyUnder = !underIndicator.add(currentThread);
        threadsUnderThisIndicator.add(alreadyUnder ? null : underIndicator);

        boolean isStandard = thisIndicator instanceof StandardProgressIndicator;
        if (!isStandard) {
          nonStandardIndicators.compute(thisIndicator, (__, count) -> {
            if (count == null) {
              return new AtomicInteger(1);
            }
            count.incrementAndGet();
            return count;
          });
          startBackgroundNonStandardIndicatorsPing();
        }

        oneOfTheIndicatorsIsCanceled = oneOfTheIndicatorsIsCanceled || thisIndicator.isCanceled();
      }

      updateThreadUnderCanceledIndicator(currentThread, oneOfTheIndicatorsIsCanceled);
    }

    try {
      return process.compute();
    }
    finally {
      synchronized (threadsUnderIndicator) {
        ProgressIndicator thisIndicator = null;
        // order doesn't matter
        for (int i = 0; i < threadsUnderThisIndicator.size(); i++) {
          thisIndicator = i == 0 ? indicator : ((WrappedProgressIndicator)thisIndicator).getOriginalProgressIndicator();
          Set<Thread> underIndicator = threadsUnderThisIndicator.get(i);
          boolean removed = underIndicator != null && underIndicator.remove(currentThread);
          if (removed && underIndicator.isEmpty()) {
            threadsUnderIndicator.remove(thisIndicator);
          }
          boolean isStandard = thisIndicator instanceof StandardProgressIndicator;
          if (!isStandard) {
            AtomicInteger newCount = nonStandardIndicators.compute(thisIndicator, (__, count) -> {
              if (count == null || count.decrementAndGet() == 0) {
                return null;
              }
              return count;
            });
            if (newCount == null) {
              stopBackgroundNonStandardIndicatorsPing();
            }
          }
          // by this time oldIndicator may have been canceled
        }
        updateThreadUnderCanceledIndicator(currentThread, oldIndicator != null && oldIndicator.isCanceled());
      }
    }
  }
private <V, E extends Throwable> V registerIndicatorAndRun(@NotNull ProgressIndicator indicator,
                                                             @NotNull Thread currentThread,
                                                             ProgressIndicator oldIndicator,
                                                             @NotNull ThrowableComputable<V, E> process)