diff --git a/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java b/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java
index 7087774fc791..1f4aaf3c80c3 100644
--- a/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java
+++ b/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java
@@ -141,9 +141,9 @@ public abstract class ProgressManager extends ProgressIndicatorProvider {
@Nullable JComponent parentComponent);
/**
- * Runs a specified process in a background thread and shows a progress dialog, which can be made non-modal by pressing
- * background button. Upon successful termination of the process a successRunnable will be called in Swing UI thread and
- * canceledRunnable will be called if terminated on behalf of the user by pressing either cancel button, while running in
+ * Runs a specified {@code process} in a background thread and shows a progress dialog, which can be made non-modal by pressing
+ * background button. Upon successful termination of the process a {@code successRunnable} will be called in Swing UI thread and
+ * {@code canceledRunnable} will be called if terminated on behalf of the user by pressing either cancel button, while running in
* a modal state or stop button if running in background.
*
* @param project the project in the context of which the operation is executed.
@@ -159,9 +159,9 @@ public abstract class ProgressManager extends ProgressIndicatorProvider {
@Nullable Runnable successRunnable,
@Nullable Runnable canceledRunnable);
/**
- * Runs a specified process in a background thread and shows a progress dialog, which can be made non-modal by pressing
- * background button. Upon successful termination of the process a successRunnable will be called in Swing UI thread and
- * canceledRunnable will be called if terminated on behalf of the user by pressing either cancel button, while running in
+ * Runs a specified {@code process} in a background thread and shows a progress dialog, which can be made non-modal by pressing
+ * background button. Upon successful termination of the process a {@code successRunnable} will be called in Swing UI thread and
+ * {@code canceledRunnable} will be called if terminated on behalf of the user by pressing either cancel button, while running in
* a modal state or stop button if running in background.
*
* @param project the project in the context of which the operation is executed.
@@ -180,7 +180,7 @@ public abstract class ProgressManager extends ProgressIndicatorProvider {
@NotNull PerformInBackgroundOption option);
/**
- * Runs a specified task in either background/foreground thread and shows a progress dialog.
+ * Runs a specified {@code task} in either background/foreground thread and shows a progress dialog.
*
* @param task task to run (either {@link Task.Modal} or {@link Task.Backgroundable}).
*/
@@ -226,7 +226,7 @@ public abstract class ProgressManager extends ProgressIndicatorProvider {
* This method attempts to run provided action synchronously in a read action, so that, if possible, it wouldn't impact any pending,
* executing or future write actions (for this to work effectively the action should invoke {@link ProgressManager#checkCanceled()} or
* {@link ProgressIndicator#checkCanceled()} often enough).
- * It returns true if action was executed successfully. It returns false if the action was not
+ * It returns {@code true} if action was executed successfully. It returns {@code false} if the action was not
* executed successfully, i.e. if:
*
* - write action was in progress when the method was called
@@ -240,17 +240,5 @@ public abstract class ProgressManager extends ProgressIndicatorProvider {
*/
public abstract boolean runInReadActionWithWriteActionPriority(@NotNull final Runnable action);
- public static boolean isInNonCancelableSection() {
- ProgressIndicator indicator = getGlobalProgressIndicator();
- while (indicator != null) {
- if (indicator instanceof NonCancelableSection) return true;
- if (indicator instanceof WrappedProgressIndicator) {
- indicator = ((WrappedProgressIndicator)indicator).getOriginalProgressIndicator();
- }
- else {
- break;
- }
- }
- return false;
- }
+ public abstract boolean isInNonCancelableSection();
}
\ No newline at end of file
diff --git a/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java b/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java
index aeb23cb6cc84..78006c7cee9c 100644
--- a/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java
+++ b/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java
@@ -21,6 +21,7 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationEx;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
@@ -50,6 +51,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class CoreProgressManager extends ProgressManager implements Disposable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.impl.CoreProgressManager");
+
static final int CHECK_CANCELED_DELAY_MILLIS = 10;
final AtomicInteger myCurrentUnsafeProgressCount = new AtomicInteger(0);
private final AtomicInteger myCurrentModalProgressCount = new AtomicInteger(0);
@@ -76,6 +79,11 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
// multiset here (instead of a set) is for simplifying add/remove indicators on process-with-progress start/end with possibly identical indicators.
private static final Collection nonStandardIndicators = ConcurrentHashMultiset.create();
+ /** true if running in non-cancelable section started with
+ * {@link #startNonCancelableSection()} or {@link #executeNonCancelableSection(Runnable)} in this thread
+ */
+ private static final ThreadLocal isInNonCancelableSection = new ThreadLocal(); // do not supply initial value to conserve memory
+
public CoreProgressManager() {
HeavyProcessLatch.INSTANCE.addUIActivityListener(new HeavyProcessLatch.HeavyProcessListener() {
@Override
@@ -206,7 +214,18 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
@Override
public void executeNonCancelableSection(@NotNull Runnable runnable) {
- executeProcessUnderProgress(runnable, NonCancelableIndicator.INSTANCE);
+ if (isInNonCancelableSection()) {
+ runnable.run();
+ }
+ else {
+ try {
+ isInNonCancelableSection.set(Boolean.TRUE);
+ executeProcessUnderProgress(runnable, NonCancelableIndicator.INSTANCE);
+ }
+ finally {
+ isInNonCancelableSection.remove();
+ }
+ }
}
@Override
@@ -656,19 +675,28 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
@NotNull
@Override
public final NonCancelableSection startNonCancelableSection() {
+ LOG.warn("Use executeNonCancelableSection() instead");
+ if (isInNonCancelableSection()) return NonCancelableSection.EMPTY;
final ProgressIndicator myOld = getProgressIndicator();
final Thread currentThread = Thread.currentThread();
- NonCancelableIndicator nonCancelor = new NonCancelableIndicator() {
+ final NonCancelableIndicator nonCancelor = new NonCancelableIndicator() {
@Override
public void done() {
setCurrentIndicator(currentThread, myOld);
+ isInNonCancelableSection.remove();
}
};
+ isInNonCancelableSection.set(Boolean.TRUE);
setCurrentIndicator(currentThread, nonCancelor);
return nonCancelor;
}
+ @Override
+ public boolean isInNonCancelableSection() {
+ return isInNonCancelableSection.get() != null;
+ }
+
@NotNull
public static ModalityState getCurrentThreadProgressModality() {
ProgressIndicator indicator = threadTopLevelIndicators.get(Thread.currentThread().getId());
@@ -710,15 +738,16 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
return myTask.toString();
}
}
- protected static class TaskRunnable extends TaskContainer {
+
+ static class TaskRunnable extends TaskContainer {
private final ProgressIndicator myIndicator;
private final Runnable myContinuation;
- public TaskRunnable(@NotNull Task task, @NotNull ProgressIndicator indicator) {
+ TaskRunnable(@NotNull Task task, @NotNull ProgressIndicator indicator) {
this(task, indicator, null);
}
- public TaskRunnable(@NotNull Task task, @NotNull ProgressIndicator indicator, @Nullable Runnable continuation) {
+ TaskRunnable(@NotNull Task task, @NotNull ProgressIndicator indicator, @Nullable Runnable continuation) {
super(task);
myIndicator = indicator;
myContinuation = continuation;
diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java
index 4321547e08fe..f1b493c2c7d2 100644
--- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java
@@ -386,7 +386,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
}
private static boolean canCancelProjectLoading() {
- return !ProgressManager.isInNonCancelableSection();
+ return !ProgressManager.getInstance().isInNonCancelableSection();
}
@Override
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java
index 1c1cfd78857b..82089f91b13f 100644
--- a/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java
@@ -635,4 +635,30 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
ApplicationManager.getApplication().invokeLater(semaphore::up);
assertTrue("invokeAndWait would deadlock", semaphore.waitFor(1000));
}
+
+ public void testNonCancelableSectionDetectedCorrectly() {
+ ProgressManager progressManager = ProgressManager.getInstance();
+ assertFalse(progressManager.isInNonCancelableSection());
+ progressManager.run(new Task.Modal(getProject(), "", false) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ assertFalse(indicator instanceof NonCancelableIndicator);
+ assertFalse(progressManager.isInNonCancelableSection());
+ progressManager.executeNonCancelableSection(() -> {
+ assertTrue(progressManager.getProgressIndicator() instanceof NonCancelableIndicator);
+ assertTrue(progressManager.isInNonCancelableSection());
+
+ progressManager.executeProcessUnderProgress(() -> {
+ assertFalse(progressManager.getProgressIndicator() instanceof NonCancelableIndicator);
+ assertTrue(progressManager.isInNonCancelableSection());
+ }, new DaemonProgressIndicator());
+
+ assertTrue(progressManager.isInNonCancelableSection());
+ });
+
+ assertFalse(progressManager.isInNonCancelableSection());
+ }
+ });
+ assertFalse(progressManager.isInNonCancelableSection());
+ }
}