From e25a7af880336e59f12e886a1a72327c0be6c7ed Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Mon, 10 Nov 2025 10:32:43 +0200 Subject: [PATCH] [UI, threading] IJPL-189188 Add @RequiresReadLock to some PsiCopyPasteManager methods Sometimes they're called directly by AWT bypassing locks. And these days locks aren't guaranteed on the EDT anyway. So let's annotate and wrap into RA/WIRA as appropriate. Mostly it's just a RA, but in the case of the paste action it's quite possible that it'll want to write something as well, so let's make it a WIRA there. GitOrigin-RevId: f9d05b1ae4691f39d9f2740173e90ee5ea8b3768 --- .../daemon/impl/PsiElementListNavigator.java | 3 +- .../com/intellij/ide/CopyPasteDelegator.java | 40 ++++++++++++------- .../com/intellij/ide/PsiCopyPasteManager.java | 6 +++ .../ide/hierarchy/HierarchyBrowserBaseEx.java | 3 +- .../impl/AbstractProjectViewPane.java | 2 +- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java index b11e8fd3e35b..cc5b76e4b88f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java @@ -6,6 +6,7 @@ import com.intellij.codeInsight.navigation.BackgroundUpdaterTask; import com.intellij.find.FindUtil; import com.intellij.ide.PsiCopyPasteManager; import com.intellij.ide.util.PsiElementListCellRenderer; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.progress.ProgressManager; @@ -245,7 +246,7 @@ public final class PsiElementListNavigator { for (int i = 0; i < selectedValues.length; i++) { copy[i] = (PsiElement)selectedValues[i]; } - return PsiCopyPasteManager.newTransferable(copy); + return ReadAction.compute(() -> PsiCopyPasteManager.newTransferable(copy)); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/CopyPasteDelegator.java b/platform/lang-impl/src/com/intellij/ide/CopyPasteDelegator.java index 0dd07b420946..525d748fcee5 100644 --- a/platform/lang-impl/src/com/intellij/ide/CopyPasteDelegator.java +++ b/platform/lang-impl/src/com/intellij/ide/CopyPasteDelegator.java @@ -2,6 +2,8 @@ package com.intellij.ide; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.WriteIntentReadAction; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbService; @@ -15,6 +17,7 @@ import com.intellij.refactoring.copy.CopyHandler; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandler; import com.intellij.util.ObjectUtils; +import com.intellij.util.concurrency.annotations.RequiresReadLock; import com.intellij.util.containers.JBIterable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -83,8 +86,10 @@ public class CopyPasteDelegator implements CopyPasteSupport { @Override public void performCopy(@NotNull DataContext dataContext) { - PsiElement[] elements = validate(getSelectedElements(dataContext)); - PsiCopyPasteManager.getInstance().setElements(elements, true); + ReadAction.run(() -> { + PsiElement[] elements = validate(getSelectedElements(dataContext)); + PsiCopyPasteManager.getInstance().setElements(elements, true); + }); updateView(); } @@ -102,13 +107,15 @@ public class CopyPasteDelegator implements CopyPasteSupport { @Override public void performCut(@NotNull DataContext dataContext) { - PsiElement[] elements = validate(getSelectedElements(dataContext)); - if (MoveHandler.adjustForMove(myProject, elements, null) == null) { - return; - } - // 'elements' passed instead of result of 'adjustForMove' because otherwise ProjectView would - // not recognize adjusted elements when graying them - PsiCopyPasteManager.getInstance().setElements(elements, false); + ReadAction.run(() -> { + PsiElement[] elements = validate(getSelectedElements(dataContext)); + if (MoveHandler.adjustForMove(myProject, elements, null) == null) { + return; + } + // 'elements' passed instead of result of 'adjustForMove' because otherwise ProjectView would + // not recognize adjusted elements when graying them + PsiCopyPasteManager.getInstance().setElements(elements, false); + }); updateView(); } @@ -125,18 +132,21 @@ public class CopyPasteDelegator implements CopyPasteSupport { @Override public void performPaste(@NotNull DataContext dataContext) { - if (!performDefaultPaste(dataContext)) { - for(PasteProvider provider: EP_NAME.getExtensionList()) { - if (provider.isPasteEnabled(dataContext)) { - provider.performPaste(dataContext); - break; + WriteIntentReadAction.run((Runnable)() -> { + if (!performDefaultPaste(dataContext)) { + for(PasteProvider provider: EP_NAME.getExtensionList()) { + if (provider.isPasteEnabled(dataContext)) { + provider.performPaste(dataContext); + break; + } } } - } + }); } @VisibleForTesting @ApiStatus.Internal + @RequiresReadLock public boolean performDefaultPaste(@NotNull DataContext dataContext) { final boolean[] isCopied = new boolean[1]; final PsiElement[] elements = PsiCopyPasteManager.getInstance().getElements(isCopied); diff --git a/platform/lang-impl/src/com/intellij/ide/PsiCopyPasteManager.java b/platform/lang-impl/src/com/intellij/ide/PsiCopyPasteManager.java index 731e4126b29e..e4621557c331 100644 --- a/platform/lang-impl/src/com/intellij/ide/PsiCopyPasteManager.java +++ b/platform/lang-impl/src/com/intellij/ide/PsiCopyPasteManager.java @@ -16,6 +16,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtilRt; +import com.intellij.util.concurrency.annotations.RequiresReadLock; import com.intellij.util.containers.JBIterable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -83,6 +84,7 @@ public final class PsiCopyPasteManager { return myRecentData.getElements(); } + @RequiresReadLock public static @NotNull Transferable newTransferable(PsiElement @NotNull ... element) { return new MyTransferable(element); } @@ -108,6 +110,7 @@ public final class PsiCopyPasteManager { myCopyPasteManager.setContents(new StringSelection("")); } + @RequiresReadLock public void setElements(PsiElement @NotNull [] elements, boolean copied) { myRecentData = new MyData(elements, copied); myCopyPasteManager.setContents(new MyTransferable(myRecentData)); @@ -217,6 +220,7 @@ public final class PsiCopyPasteManager { myDataProxy = data; } + @RequiresReadLock public MyTransferable(PsiElement @NotNull [] selectedValues) { this(new PsiCopyPasteManager.MyData(selectedValues, true)); } @@ -277,6 +281,7 @@ public final class PsiCopyPasteManager { } } + @RequiresReadLock public static @Nullable List asFileList(PsiElement[] elements) { List result = new ArrayList<>(); for (PsiElement element : elements) { @@ -288,6 +293,7 @@ public final class PsiCopyPasteManager { return result.isEmpty() ? null : result; } + @RequiresReadLock public static @Nullable VirtualFile asVirtualFile(@Nullable PsiElement element) { PsiFileSystemItem psiFile = null; if (element instanceof PsiFileSystemItem) { diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java index d43207b9835c..d678aa703fb0 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java @@ -20,6 +20,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ComboBoxAction; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.PlatformEditorBundle; import com.intellij.openapi.fileEditor.PsiElementNavigatable; @@ -281,7 +282,7 @@ public abstract class HierarchyBrowserBaseEx extends HierarchyBrowserBase implem @Override public List asFileList() { - return PsiCopyPasteManager.asFileList(getPsiElements()); + return ReadAction.compute(() -> PsiCopyPasteManager.asFileList(getPsiElements())); } }); } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java index 3e32367bb709..93159e3b7971 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java @@ -971,7 +971,7 @@ public abstract class AbstractProjectViewPane implements UiCompatibleDataProvide return new DnDDragStartBean(new TransferableWrapper() { @Override public List asFileList() { - return PsiCopyPasteManager.asFileList(psiElements); + return ReadAction.compute(() -> PsiCopyPasteManager.asFileList(psiElements)); } @Override