transactions: minor javadoc updates, store transaction start stack trace for easier diagnostics

This commit is contained in:
peter
2016-03-04 18:08:26 +01:00
parent 9333105fa6
commit 169495d971
3 changed files with 18 additions and 14 deletions
@@ -53,11 +53,12 @@ import org.jetbrains.annotations.NotNull;
* {@link #submitMergeableTransaction(TransactionKind, Runnable)} call.<p/>
*
* Q: I've got <b>"Nested transactions are not allowed"</b> exception, what do I do?<br/>
* 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)}).
@@ -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.*
@@ -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<Runnable> myQueue = new LinkedBlockingQueue<Runnable>();
private final Set<TransactionKind> 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 {