allow nested synchronous transactions in the same modality

This commit is contained in:
peter
2016-03-17 19:14:12 +01:00
parent 6e39023269
commit 51d0a85306
2 changed files with 24 additions and 10 deletions
@@ -36,24 +36,40 @@ public class TransactionGuardImpl extends TransactionGuard {
private final Queue<Runnable> myQueue = new LinkedBlockingQueue<Runnable>();
private final Set<TransactionKind> myMergeableKinds = ContainerUtil.newHashSet();
private String myTransactionStartTrace;
private ModalityState myTransactionModality;
@Override
@NotNull
public AccessToken startSynchronousTransaction(@NotNull TransactionKind kind) throws IllegalStateException {
ApplicationManager.getApplication().assertIsDispatchThread();
if (myTransactionStartTrace != null) {
if (!myMergeableKinds.contains(kind) && !ApplicationManager.getApplication().isUnitTestMode()) {
// please assign exceptions that occur here to Peter
LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind,
new Attachment("trace.txt", myTransactionStartTrace));
ModalityState modality = ModalityState.current();
if (isInsideTransaction()) {
if (myTransactionModality == modality) {
return AccessToken.EMPTY_ACCESS_TOKEN;
}
if (myMergeableKinds.contains(kind)) {
final ModalityState prev = myTransactionModality;
myTransactionModality = modality;
return new AccessToken() {
@Override
public void finish() {
myTransactionModality = prev;
}
};
}
// please assign exceptions that occur here to Peter
LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind,
new Attachment("trace.txt", myTransactionStartTrace));
return AccessToken.EMPTY_ACCESS_TOKEN;
}
myTransactionModality = modality;
myTransactionStartTrace = DebugUtil.currentStackTrace();
return new AccessToken() {
@Override
public void finish() {
myTransactionStartTrace = null;
myTransactionModality = null;
if (!myQueue.isEmpty()) {
pollQueueLater();
}
@@ -148,7 +164,7 @@ public class TransactionGuardImpl extends TransactionGuard {
public void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Runnable transaction) throws ProcessCanceledException {
Application app = ApplicationManager.getApplication();
if (app.isDispatchThread()) {
if (!canRunTransactionNow(kind)) {
if (!canRunTransactionNow(kind) && myTransactionModality != ModalityState.current()) {
throw new AssertionError("Cannot run submitTransactionAndWait from another transaction, kind " + kind + " is not allowed");
}
runSyncTransaction(kind, transaction);
@@ -94,9 +94,7 @@ public class EndHandler extends EditorActionHandler {
// here just as a boolean value holder due to requirement to declare variable used from inner class as final.
final AtomicBoolean stopProcessing = new AtomicBoolean(true);
TransactionGuard guard = TransactionGuard.getInstance();
// sometimes this handler is invoked from other actions, then we're already inside a transaction
try (AccessToken ignore = guard.isInsideTransaction() ? null : guard.startSynchronousTransaction(TransactionKind.TEXT_EDITING)) {
try (AccessToken ignore = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.TEXT_EDITING)) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
ApplicationManager.getApplication().runWriteAction(() -> {
CodeStyleManager styleManager = CodeStyleManager.getInstance(project);