Runnable (@NotNull ProcessHandler processHandler, @Nullable BooleanSupplier cancelableFun) { return new Runnable() { private ProgressIndicator myProgressIndicator; private final Semaphore mySemaphore = new Semaphore(); private final Runnable myWaitThread = () -> { try { processHandler.waitFor(); } finally { mySemaphore.up(); } }; private final Runnable myCancelListener = new Runnable() { @Override public void run() { while (true) { if (myProgressIndicator != null && (myProgressIndicator.isCanceled() || !myProgressIndicator.isRunning()) || cancelableFun != null && cancelableFun.getAsBoolean() || processHandler.isProcessTerminated()) { if (!processHandler.isProcessTerminated()) { try { processHandler.destroyProcess(); } finally { mySemaphore.up(); } } break; } try { synchronized (this) { wait(1000); } } catch (InterruptedException e) { //Do nothing } } } }; @Override public void run() { myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); if (myProgressIndicator != null && StringUtil.isEmpty(myProgressIndicator.getText())) { myProgressIndicator.setText(ExecutionBundle.message("please.wait")); } LOG.assertTrue(myProgressIndicator != null || cancelableFun != null, "Cancelable process must have an opportunity to be canceled!"); mySemaphore.down(); ApplicationManager.getApplication().executeOnPooledThread(myWaitThread); ApplicationManager.getApplication().executeOnPooledThread(myCancelListener); OSProcessHandler.checkEdtAndReadAction(processHandler); mySemaphore.waitFor(); } }; }