Fix UI Freezes: Avoid Non-Cancellable Read Actions in Plugins

Fix UI Freezes: Avoid Non-Cancellable Read Actions in Plugins

Understanding UI Freezes in JetBrains IDEs

JetBrains IDEs are powerful tools for developers, but even the best software can face performance hiccups. One common issue users report is UI freezes—those frustrating moments when the interface becomes unresponsive. While many blame heavy operations on the Event Dispatch Thread (EDT), our investigations reveal a hidden culprit: non-cancellable read actions running in background threads.

Why Non-Cancellable Read Actions Cause Problems

Plugins often use ReadAction.compute to access PSI (Program Structure Interface) data safely. However, when these actions are non-cancellable and run in background threads, they can block the EDT indirectly. This creates a deadlock scenario where the UI waits for a computation that can’t be interrupted, leading to freezes.

How It Happens

  • Background thread starts a ReadAction to process data.
  • The action performs long-running I/O or computation without cancellation support.
  • The EDT waits for the result, but the background task can’t be interrupted.
  • UI becomes unresponsive until the task completes.

Real-World Example: Package Checker Plugin

A freeze report from the Package Checker plugin illustrates this issue. The stack trace shows a background thread (DefaultDispatcher-worker-27) executing ReadAction.compute while the EDT waits for it to finish. This creates a circular dependency that blocks the UI.

Stack Trace Analysis

"DefaultDispatcher-worker-27" prio=0 tid=0x0 nid=0x0 runnable

java.lang.Thread.State: RUNNABLE

at [email protected]/java.io.WinNTFileSystem.getBooleanAttributes0(Native Method)

at [email protected]/java.io.File.exists(File.java:874)

at com.intellij.openapi.vfs.newvfs.impl.VirtualFileImpl.exists(VirtualFileImpl.java:123)

at com.intellij.psi.impl.PsiManagerImpl.dropPsiCaches(PsiManagerImpl.java:108)

...

How to Fix Non-Cancellable Read Actions

To prevent UI freezes, follow these best practices:

1. Make Read Actions Cancellable

Wrap long-running operations in ReadAction with cancellation support. Use ProgressIndicator to check for cancellation requests:

ReadAction.run(() -> {

if (progress.isCanceled()) return null;

// Perform computation
});

2. Avoid Blocking the EDT

Use ApplicationManager.getApplication().invokeLater() for UI updates instead of blocking the EDT. For background tasks, prefer WriteAction with proper thread management.

3. Optimize File I/O

Replace synchronous file operations with asynchronous alternatives. For example, use VirtualFileManager APIs that support non-blocking I/O.

Best Practices for Plugin Developers

  • Test with large projects to identify performance bottlenecks.
  • Profile plugins using JetBrains Profiler to detect deadlocks.
  • Follow the Read-Write Thread Rules from the JetBrains documentation.

Conclusion: Keep Your Plugins Responsive

Non-cancellable read actions in background threads are a silent performance killer. By making your plugin’s computations cancellable and avoiding EDT blocking, you can ensure smooth user experiences. Always test your code under load and use JetBrains’ built-in tools to catch issues early.

Ready to optimize your plugin? Review your code for non-cancellable ReadAction calls today and eliminate UI freezes before they happen.