void (final XExecutionStackContainer container) { myDebugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(this) { final Set<ThreadReferenceProxyImpl> myAddedThreads = new HashSet<>(); @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) { List<ThreadReferenceProxyImpl> pausedThreads = StreamEx.of(myDebugProcess.getSuspendManager().getPausedContexts()) .map(SuspendContextImpl::getThread) .nonNull() .toList(); // add paused threads first CompletableFuture.completedFuture(pausedThreads) .thenCompose(tds -> addThreads(tds, THREAD_NAME_COMPARATOR, false)) .thenCompose(res -> res ? getDebugProcess().getVirtualMachineProxy().allThreadsAsync() : CompletableFuture.completedFuture(Collections.emptyList())) .thenAccept(tds -> addThreads(tds, THREADS_SUSPEND_AND_NAME_COMPARATOR, true)) .exceptionally(throwable -> DebuggerUtilsAsync.logError(throwable)); } CompletableFuture<Boolean> addThreads(Collection<ThreadReferenceProxyImpl> threads, @Nullable Comparator<? super JavaExecutionStack> comparator, boolean last) { List<CompletableFuture<JavaExecutionStack>> futures = new ArrayList<>(); for (ThreadReferenceProxyImpl thread : threads) { if (container.isObsolete()) { return CompletableFuture.completedFuture(false); } if (thread != null && myAddedThreads.add(thread)) { futures.add(JavaExecutionStack.create(thread, myDebugProcess, thread == myThread)); } } return DebuggerUtilsAsync.reschedule(CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))).thenApply(__ -> { if (!container.isObsolete()) { StreamEx<JavaExecutionStack> stacks = StreamEx.of(futures).map(CompletableFuture::join); if (comparator != null) { stacks = stacks.sorted(comparator); } container.addExecutionStack(stacks.toList(), last); } return true; }); } }); }