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 7291d411e241..423684a31408 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java @@ -17,6 +17,8 @@ package com.intellij.openapi.application; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NotNull; /** @@ -96,14 +98,27 @@ public abstract class TransactionGuard { } /** - * Schedules a transaction and waits for it to be completed. Only allowed to be invoked on non-UI thread and outside read action. + * Schedules a transaction and waits for it to be completed. Fails if invoked on UI thread inside an incompatible transaction, + * or inside a read action on non-UI thread. * @see #submitMergeableTransaction(TransactionKind, Runnable) - * @param kind - * @param transaction * @throws ProcessCanceledException if current thread is interrupted */ public abstract void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull Runnable transaction) throws ProcessCanceledException; + /** + * Same as {@link #submitTransactionAndWait(TransactionKind, Runnable)}, but returns a value computed by the transaction. + */ + public T submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Computable transaction) throws ProcessCanceledException { + final Ref result = Ref.create(); + submitTransactionAndWait(kind, new Runnable() { + @Override + public void run() { + result.set(transaction.compute()); + } + }); + return result.get(); + } + /** * A synchronous version of {@link #submitMergeableTransaction(TransactionKind, Runnable)}. * @return a token object for this transaction. Call {@link AccessToken#finish()} (inside finally) when the transaction is complete. 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 eb4b02eec959..43ca3fb43d63 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -48,7 +48,6 @@ public class TransactionGuardImpl extends TransactionGuard { // 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)); - //throw new IllegalStateException("Nested transactions are not allowed"); } myTransactionStartTrace = DebugUtil.currentStackTrace(); return new AccessToken() { @@ -99,7 +98,7 @@ public class TransactionGuardImpl extends TransactionGuard { Runnable runnable = new Runnable() { @Override public void run() { - if (!isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) { + if (canRunTransactionNow(kind)) { runSyncTransaction(kind, transaction); } else { @@ -118,6 +117,10 @@ public class TransactionGuardImpl extends TransactionGuard { } } + protected boolean canRunTransactionNow(@NotNull TransactionKind kind) { + return !isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind); + } + @Override @NotNull public AccessToken acceptNestedTransactions(TransactionKind... kinds) { @@ -144,9 +147,15 @@ public class TransactionGuardImpl extends TransactionGuard { @Override public void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Runnable transaction) throws ProcessCanceledException { Application app = ApplicationManager.getApplication(); - assert !app.isDispatchThread() : "submitTransactionAndWait should not be invoked on dispatch thread"; - assert !app.isReadAccessAllowed() : "submitTransactionAndWait should not be invoked from a read action"; + if (app.isDispatchThread()) { + if (!canRunTransactionNow(kind)) { + throw new AssertionError("Cannot run submitTransactionAndWait from another transaction, kind " + kind + " is not allowed"); + } + runSyncTransaction(kind, transaction); + return; + } + assert !app.isReadAccessAllowed() : "submitTransactionAndWait should not be invoked from a read action"; final Semaphore semaphore = new Semaphore(); semaphore.down(); final Throwable[] exception = {null};