Changes in threading model in IntelliJ Platform 2025.1

In the IntelliJ Platform, one of our goals is to reduce access to the model (i.e., PSI and VFS) from the Event Dispatch Thread (EDT): see https://youtrack.jetbrains.com/issue/IJPL-53.
To achieve this, we are relaxing certain contracts that were historically enforced. For version 2025.1, the following changes are in effect:

  1. Swing invocation events (i.e., SwingUtilities.invokeLater and SwingUtilities.invokeAndWait) are no longer wrapped into the Write-Intent lock. This means that if you access the model through these functions, you must use explicit threading actions (ReadAction.run or WriteAction.run, depending on the use case). See https://youtrack.jetbrains.com/issue/IJPL-149317
  2. Dispatchers.Main no longer wraps coroutines into the Write-Intent lock. You can either move model access from Dispatchers.Main to Dispatchers.Default, or use Dispatchers.EDT, which preserves the old behavior. See https://youtrack.jetbrains.com/issue/IJPL-178581 or https://plugins.jetbrains.com/docs/intellij/coroutine-dispatchers.html
  3. We are introducing a function edtWriteAction, which is a stable version of writeAction. The contract of edtWriteAction explicitly includes the thread of execution. See https://youtrack.jetbrains.com/issue/IJPL-149765 for our future plans regarding write actions.
3 Likes

Hi @konstantin.nisht

I checked the linked issue but I was wondering if there are any examples of how the SwingUtilities call should be converted to.

All those changes will take place in the upcoming 2025.1 Release?

Thanks

If my understanding is correct, we’ll see fewer Slow operations are prohibited on EDT warnings and other EDT and Write-lock warnings/errors (without modifying our code)?

Hi Jonathan
Suppose you have the following code:

SwingUtilities.invokeLater (() -> {
  JavaPsiFacade.getInstance(project).findClass("java.lang.String");
});

Then the old contracts can be preserved with either

ApplicationManager.getApplication().invokeLater(() -> {
  JavaPsiFacade.getInstance(project).findClass("java.lang.String");
});

or

SwingUtilities.invokeLater (() -> {
  ReadAction.run(() -> {
    JavaPsiFacade.getInstance(project).findClass("java.lang.String");
  });
});

The latter is preferable here, as Application.invokeLater will wrap runnables into a lock, so you would be able to access the model safely. By using SwingUtilities.invokeLater, you indicate that you are working with UI, so the model access may be unexpected there.

Yes, these changes are coming in 2025.1. They are not present in 2024.3.

1 Like

Hi Jonathan,
Regarding slow operations, that’s not the case, unfortunately; but we are doing steps in this direction. It is unlikely that these errors disappear without any action, as we need to maintain the contracts of the existing code.
Regarding other EDT assertions, you may have seen an error that starts like this Access is allowed with explicit read lock.. In 2025.1, this error disappears, as we chose a different approach for liberating UI from model access. Our roadmap is here https://youtrack.jetbrains.com/issue/IJPL-178593

1 Like

Thanks for the clarification.
Maybe a dump question but if I am using the invoke later meaning I am using the ui so isn’t directly required a write and not a read?

Also are we expecting the verification to fail on those issue?

Thanks again

Not sure if I understood your questions correctly.

if I am using the invoke later meaning I am using the ui so isn’t directly required a write and not a read?

In the ideal world, Application.invokeLater or SwingUtilities.invokeLater should work only with UI, as the main contract of these methods is that they are executing on the UI thread. Any attempt to perform complex computations may result in freezes.
Unfortunately, it is not easy to avoid working with the model on UI thread, as we have a historical contract that write actions must run only on the UI thread. We are working in the direction of allowing write actions on background threads

Also are we expecting the verification to fail on those issue?

Normally, the methods which use PSI have assertions inside them, like Application.assertReadAccessAllowed. These assertions will fail within SwingUtilities.invokeLater, but not in Application.invokeLater.

Ok so just to check I understand:
If a SwingUtilities.invokeLater call performs a basic UI operation (e.g., repaint, component modification) independent of the PSI, no changes are required. The sole requirement is to ensure the code executes on the EDT - is that right?

Yes, that’s right.
Anyway, if you accidentally access PSI, there will be an assertion and you’ll know that you need ReadAction here.

2 Likes