diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java index 54841737ca3f..7291d411e241 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java @@ -53,11 +53,12 @@ import org.jetbrains.annotations.NotNull; * {@link #submitMergeableTransaction(TransactionKind, Runnable)} call.

* * Q: I've got "Nested transactions are not allowed" exception, what do I do?
- * A: First, find the place in the stack where the outer transaction is started. Then, see if there is any Swing event pumping - * in between two transactions (e.g. a dialog is shown). If not, one of two transactions is superfluous, remove it. If there - * is event pumping, check if the client code (e.g. the one showing the dialog) is prepared to the nested model modifications - * of the specified kinds. For example, refactoring dialogs might be prepared to {@link TransactionKind#TEXT_EDITING} kind - * (for text field editing inside the dialogs) but not + * A: First, identify the place in the stack where the outer transaction is started (the exception attachment should contain it). + * Then, see if there is any Swing event pumping + * in between two transactions (e.g. a dialog is shown). If not, one of the two involved transactions is superfluous, remove it. If there + * is event pumping present, check if the client code (e.g. the one showing the dialog) is prepared to such nested model modifications. + * For example, refactoring dialogs might be prepared to {@link TransactionKind#TEXT_EDITING} kind + * (for text field editing inside the dialogs) but not to * other model changes, e.g. root changes. The outer transaction code might then specify which kinds it's prepared to (by using * {@link #acceptNestedTransactions(TransactionKind...)}), and the inner transaction code should have the very same transaction kind * (by using {@link #submitMergeableTransaction(TransactionKind, Runnable)} or {@link #startSynchronousTransaction(TransactionKind)}). diff --git a/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java b/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java index 711dc54680e4..b42f547b326f 100644 --- a/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java +++ b/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java @@ -3,7 +3,7 @@ package com.intellij.openapi.application; import java.lang.annotation.*; /** - * Add this annotation to actions (AnAction inheritors) to make them run inside a transaction. + * Add this annotation to actions (AnAction inheritors) to make them run inside a synchronous transaction. * * @see TransactionGuard * @since 146.* diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 3d627e9956f0..70a54e4886a0 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -15,8 +15,10 @@ */ package com.intellij.openapi.application; +import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.psi.impl.DebugUtil; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -33,7 +35,7 @@ public class TransactionGuardImpl extends TransactionGuard { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.TransactionGuardImpl"); private final Queue myQueue = new LinkedBlockingQueue(); private final Set myMergeableKinds = ContainerUtil.newHashSet(); - private boolean myInsideTransaction; + private String myTransactionStartTrace; @Override @NotNull @@ -42,15 +44,16 @@ public class TransactionGuardImpl extends TransactionGuard { if (kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) { return AccessToken.EMPTY_ACCESS_TOKEN; } - if (myInsideTransaction) { - LOG.error("Nested transactions are not allowed"); + if (myTransactionStartTrace != null) { + LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment.", + new Attachment("trace.txt", myTransactionStartTrace)); //throw new IllegalStateException("Nested transactions are not allowed"); } - myInsideTransaction = true; + myTransactionStartTrace = DebugUtil.currentStackTrace(); return new AccessToken() { @Override public void finish() { - myInsideTransaction = false; + myTransactionStartTrace = null; if (!myQueue.isEmpty()) { pollQueueLater(); } @@ -64,7 +67,7 @@ public class TransactionGuardImpl extends TransactionGuard { app.invokeLater(new Runnable() { @Override public void run() { - if (myInsideTransaction) return; + if (isInsideTransaction()) return; Runnable next = myQueue.poll(); if (next != null) { @@ -87,7 +90,7 @@ public class TransactionGuardImpl extends TransactionGuard { @Override public boolean isInsideTransaction() { ApplicationManager.getApplication().assertIsDispatchThread(); - return myInsideTransaction; + return myTransactionStartTrace != null; } @Override @@ -95,7 +98,7 @@ public class TransactionGuardImpl extends TransactionGuard { Runnable runnable = new Runnable() { @Override public void run() { - if (!myInsideTransaction || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) { + if (!isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) { runSyncTransaction(kind, transaction); } else {