@TestOnly
  public static void awaitQuiescence(@NotNull ThreadPoolExecutor executor, long timeout, @NotNull TimeUnit unit) {
    executor.setKeepAliveTime(1, TimeUnit.NANOSECONDS); // no need for zombies in tests
    executor.setCorePoolSize(0); // interrupt idle workers
    ReentrantLock mainLock = ReflectionUtil.getField(executor.getClass(), executor, ReentrantLock.class, "mainLock");
    Set<Object> workers;
    mainLock.lock();
    try {
      @SuppressWarnings("unchecked")
      Set<Object> workersField = ReflectionUtil.getField(executor.getClass(), executor, HashSet.class, "workers");
      // to be able to iterate thread-safely outside the lock
      workers = new HashSet<>(workersField);
    }
    finally {
      mainLock.unlock();
    }
    for (Object worker : workers) {
      Thread thread = ReflectionUtil.getField(worker.getClass(), worker, Thread.class, "thread");
      try {
        thread.join(unit.toMillis(timeout));
      }
      catch (InterruptedException e) {
        @NonNls String trace = "Thread leaked: " + thread + "; " + thread.getState() + " (" + thread.isAlive() + ")\n--- its stacktrace:\n";
        for (final StackTraceElement stackTraceElement : thread.getStackTrace()) {
          trace += " at "+stackTraceElement+"\n";
        }
        trace += "---\n";
        @NonNls String message = "Executor " + executor + " is still active after " + unit.toSeconds(timeout) + " seconds://///\n" +
                                 "Thread " + thread + " dump:\n" + trace +
                                 "all thread dump:\n" + ThreadDumper.dumpThreadsToString() + "\n/////";
        System.err.println(message);
        break;
      }
    }
  }
@TestOnly
  public static void awaitQuiescence(@NotNull ThreadPoolExecutor executor, long timeout, @NotNull TimeUnit unit)