void () { int parallelism = JobSchedulerImpl.getJobPoolParallelism(); // values higher than this can lead to too coarse tasks in FJP queue which means some of them may contain more than one element which means long-delay task and some short-delay tasks can be scheduled to one chunk which means some of the tasks will not be stolen for a (relatively)long time. // so it's a TODO for ApplierCompleter int N = 1 << parallelism; Integer[] times = new Integer[N]; Arrays.fill(times, 0); int longDelay = (int)TimeUnit.MINUTES.toMillis(2); for (int i=0; i<100; i++) { times[(i + N - 1) % N] = 0; times[i % N] = longDelay; AtomicInteger executed = new AtomicInteger(); DaemonProgressIndicator progress = new DaemonProgressIndicator(); UsefulTestCase.assertThrows(MyException.class, "myMsg", () -> { TestTimeOut deadline = setTimeout(2, TimeUnit.SECONDS); JobLauncher.getInstance().invokeConcurrentlyUnderProgress(Arrays.asList(times.clone()), progress, time -> { while ((time -= 100) >= 0) { ProgressManager.checkCanceled(); TimeoutUtil.sleep(100); if (deadline.isTimedOut()) { String s = ThreadDumper.dumpThreadsToString(); throw new AssertionError("Timed out at " + time + "; parallelism:"+parallelism+"; executed:"+executed+"; threads:\n" + s); } } if (executed.incrementAndGet() >= times.length - 1) { // executed all but the slowest one throw new MyException("myMsg"); } return true; }); }); } }