mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[cancellation] IJPL-236372: Do not allow resurrection of job-bridging progress indicators
(cherry picked from commit a591d2cc33015eab4c75b4227060b8854ad3edb4) IJ-CR-193221 GitOrigin-RevId: c4ed067bdac37147c7fc2e84b130253b44f2bde6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b148cf89ee
commit
09df581bea
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.progress
|
||||
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* A base class for progress indicators that depend on an instance of a job.
|
||||
*
|
||||
* The motivation is the following:
|
||||
* ```kotlin
|
||||
* coroutineToIndicator { indicator ->
|
||||
* runProcess({}, indicator)
|
||||
* }
|
||||
* ```
|
||||
* Here, [ProgressManager.runProcess] calls [ProgressIndicator.start] on `indicator`,
|
||||
* which would lead to removal of cancellations status for [EmptyProgressIndicator]. To avoid it, we reimplement [EmptyProgressIndicatorBase] directly
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Suppress("UsagesOfObsoleteApi")
|
||||
internal abstract class BridgeJobIndicatorBase(modalityState: ModalityState): EmptyProgressIndicatorBase(modalityState), StandardProgressIndicator {
|
||||
companion object {
|
||||
private val PLACEHOLDER: Throwable = Throwable(
|
||||
"Dummy throwable that indicates cancellation of EmptyProgressIndicator.\nSet `ide.rich.cancellation.traces` to `true` to get real origin of cancellation.")
|
||||
}
|
||||
|
||||
@Volatile
|
||||
private var cancellationCause: Throwable? = null
|
||||
|
||||
override fun cancel() {
|
||||
cancellationCause = if (Registry.`is`("ide.rich.cancellation.traces", false)) {
|
||||
Throwable("Origin of cancellation of $this")
|
||||
}
|
||||
else {
|
||||
PLACEHOLDER
|
||||
}
|
||||
}
|
||||
|
||||
override fun isCanceled(): Boolean {
|
||||
return cancellationCause != null
|
||||
}
|
||||
|
||||
override fun getCancellationCause(): Throwable? {
|
||||
return cancellationCause.takeIf { it != PLACEHOLDER }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.intellij.openapi.progress;
|
||||
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.ApiStatus.Obsolete;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -42,8 +43,14 @@ public class EmptyProgressIndicator extends EmptyProgressIndicatorBase implement
|
||||
ProgressManager.canceled(this);
|
||||
}
|
||||
|
||||
final @Nullable Throwable getCancellationCause() {
|
||||
return myCancellationRequester;
|
||||
@ApiStatus.Internal
|
||||
@Override
|
||||
protected final @Nullable Throwable getCancellationCause() {
|
||||
if (myCancellationRequester != PLACEHOLDER) {
|
||||
return myCancellationRequester;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+10
-3
@@ -3,8 +3,10 @@ package com.intellij.openapi.progress;
|
||||
|
||||
import com.intellij.diagnostic.PluginException;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.ApiStatus.Obsolete;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -72,14 +74,19 @@ public abstract class EmptyProgressIndicatorBase implements ProgressIndicator {
|
||||
public final void checkCanceled() {
|
||||
if (isCanceled() && myNonCancelableSectionCount == 0 && !Cancellation.isInNonCancelableSection()) {
|
||||
ProcessCanceledException e = new ProcessCanceledException();
|
||||
if (this instanceof EmptyProgressIndicator) {
|
||||
Throwable cause = Objects.requireNonNull(((EmptyProgressIndicator)this).getCancellationCause());
|
||||
e.addSuppressed(cause);
|
||||
Throwable cancellationCause = getCancellationCause();
|
||||
if (cancellationCause != null) {
|
||||
e.addSuppressed(cancellationCause);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected @Nullable Throwable getCancellationCause() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String text) { }
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import com.intellij.platform.util.progress.RawProgressReporter
|
||||
internal class RawProgressReporterIndicator(
|
||||
private val reporter: RawProgressReporter,
|
||||
contextModality: ModalityState,
|
||||
) : EmptyProgressIndicator(contextModality) {
|
||||
|
||||
) : BridgeJobIndicatorBase(contextModality) {
|
||||
override fun setText(text: String?) {
|
||||
reporter.text(text)
|
||||
}
|
||||
|
||||
@@ -558,7 +558,7 @@ private fun <T> contextToIndicator(ctx: CoroutineContext, action: () -> T): T {
|
||||
* We keep this class as an inheritor of [EmptyProgressIndicator] for ease of debugging -- if one sees an instance of this class,
|
||||
* it means that the currently used indicator depends on some job.
|
||||
*/
|
||||
private class JobDependentIndicator(modalityState: ModalityState): EmptyProgressIndicator(modalityState)
|
||||
private class JobDependentIndicator(modalityState: ModalityState): BridgeJobIndicatorBase(modalityState)
|
||||
|
||||
@Throws(CancellationException::class)
|
||||
@Internal
|
||||
|
||||
Reference in New Issue
Block a user