How can I use a synchronous modal progress indicator on the EDT when the write lock is active?

My plugin implements ModuleBuilder and WebProjectTemplate to provide new project wizards in IntelliJ IDEA and WebStorm/etc. respectively. When createModule() is called in the former or generateProject() is called in the latter, my plugin must execute a command-line task to populate the project directory structure with the appropriate files. That command-line task may take several seconds to complete, so I’ve tried to have it execute under a modal progress indicator.

The problem is that ApplicationImpl#runProcessWithProgressSynchronously() does the following:

    // disallow running a process in a separate thread from a write-action, or a thread will deadlock trying to acquire the read-lock
    if (isDispatchThread() && isWriteAccessAllowed()) {
      getLogger().debug("Starting process with progress from within write action makes no sense");
      try {
        ProgressManager.getInstance().runProcess(process, new EmptyProgressIndicator());
      }
      catch (ProcessCanceledException e) {
        // ok to ignore.
        return false;
      }

As a result, there’s no actual end user-facing progress indicator. However, I can’t help that createModule()/generateProject() is invoked on the EDT under the write lock as that’s just how these wizards operate.

The only workaround I’ve found is to wrap my CLI-invocation-under-progress-indicator in ApplicationImpl#executeSuspendingWriteAction(), but both the class and method are annotated as @Internal, so I’m guessing that will cause issues with publishing new versions of my plugin.

What other options exist to accomplish this, specifically in Java? Or is the only way to accomplish this via sanctioned portions of the plugin SDK to use Kotlin coroutines?

Thanks in advance for any thoughts.

I would heavily recommend moving it to a later stage when when project already opened, e.g. in Project Activity. It is much better UX than blocking everything on click

Okay, so it sounds like nested in that response is that I likely couldn’t be granted an allowance in my plugin for calling runProcessWithProgressSynchronously() executeSuspendingWriteAction() in two specific spots where I run this command-line task during the new project wizards, correct? The alternative you’ve recommended is a pretty significant change, enough so that I’d likely just stick with the current (relatively short) EDT freeze while the command-line task executes.

It is allowed, we only marked it Obsolete so people think a bit about its usages

Consider getting rid of a modal progress altogether, for example, by using a background progress

Even in your situation if CLI does not respond (for some reason) there will be non-cancellable hard freeze

Sorry, I typed the wrong thing. I meant executeSuspendingWriteAction() which is annotated as @ApiStatus.Internal. Apologies for the confusion. I’ll correct my earlier response for clarity.

But it does not make sense in your situation, as it may not run on EDT anyway.

We for instance still use ProgressManager.getInstance().runProcessWithProgressSynchronously to get ZIPs from start.spring.io before we proceed to project opening

1 Like

Yeah, let me think through it a bit. I get the core point of execution of an external command-line process on the EDT being a bad thing. Having it run under a modal progress indicator “fixes” that because it’s cancelable (and all of my CLI invocations are properly cancelable, by the way) and it doesn’t freeze the EDT…at least in the sense that the modal dialog does present status and respond to end user input on a second event thread. But at its core, it’s incorrect. I’ll chew on it. Thanks for the feedback!

Yep, just as you suggested, deferring the heavy-lifting until after the new project has opened was pretty simple and provides a much better UX. Thanks for steering me in the right direction!