coroutine_execution_contexts.md: Cleaner APIs presentation

This commit is contained in:
Karol Lewandowski 2025-02-18 13:02:37 +01:00 committed by Karol Lewandowski
parent f0a2559be2
commit f548a2bc2c

View File

@ -1,28 +1,24 @@
<!-- Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
# Execution Contexts
<primary-label ref="2024.1"/>
<link-summary>Tracking execution progress, checking for cancellations, and switching between different execution contexts.</link-summary>
<include from="coroutines_snippets.md" element-id="learnCoroutines"/>
The IntelliJ Platform provides APIs that allow tracking the progress of background processes and canceling their execution when they are canceled by a user, or they become obsolete due to some changes in the data model.
Background processes can be executed in three contexts:
- [](#suspending-context)
- [](#suspending-context-coroutines) — available since 2024.1
- [](#blocking-context)
- [](#progress-indicator) (obsolete since 2024.1)
- [](#progress-indicator) — obsolete since 2024.1
Currently, the Progress Indicator context is the most widely used approach in the IntelliJ Platform.
As the platform's execution model moves towards coroutines, this approach can be considered obsolete.
Starting with 2024.1, it is recommended to execute new code in the [suspending context](#suspending-context).
Once the client code switches to a suspending or a blocking context, it should not switch back to a progress indicator context.
As the platform's execution model moves towards [coroutines](launching_coroutines.md), this approach can be considered obsolete.
Starting with 2024.1, it is recommended to execute new code in the [suspending context](#suspending-context-coroutines).
The following sections explain the contexts and provide information about process cancellation, progress tracking, and switching between different contexts.
## Suspending Context
## Suspending Context (Coroutines)
<primary-label ref="2024.1"/>
Code [executed in Kotlin coroutines](launching_coroutines.md) is executed in a suspending context.
Since 2024.1, this context is recommended for executing background tasks to maximize CPU utilization.
@ -32,55 +28,17 @@ Since 2024.1, this context is recommended for executing background tasks to maxi
{style="warning"}
In a suspending context, methods such as `ProgressManager.checkCanceled()` or `ModalityState.defaultModalityState()` won't have any effect.
Therefore, if their behavior is required, [switch to a blocking context](#suspending-context-switching-to-other-contexts).
Therefore, if their behavior is required, [switch to a blocking context](#switching-between-contexts).
> Inspection <control>Plugin DevKit | Code | Forbidden in suspend context method usage</control> reports calling blocking code from suspending context.
### Cancellation Check
{#suspending-context-cancellation-check}
- [`ensureActive()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-active.html) from Kotlin coroutine's API
> Note that [`ProgressManager.checkCanceled()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) does not work in a coroutine context.
>
{style="warning"}
### Progress Reporting
{#suspending-context-progress-reporting}
- [`ProgressStep`](%gh-ic%/platform/util/progress/src/impl/ProgressStep.kt) - a step-based progress reporting (see its KDoc for details)
- [`RawProgressReporter`](%gh-ic%/platform/util/progress/src/RawProgressReporter.kt) - a raw text, details, and fraction reporting (invoked via [`reportRawProgress()`](%gh-ic%/platform/util/progress/src/steps.kt))
Any [`report*Progress()`](%gh-ic%/platform/util/progress/src/steps.kt) function must be used inside [`withBackgroundProgress()`, `withModalProgress()`, or `runWithModalProgressBlocking()`](%gh-ic%/platform/progress/shared/src/tasks.kt).
Otherwise, if there is no reporter in the context, using `report*Progress()` will have no effect.
Example:
```kotlin
withBackgroundProgress(...) { // or withModalProgress/runWithModalProgressBlocking
// ...
reportProgress { reporter -> // or another report*Progress
// do tasks and report progress
}
// ...
}
```
### Switching to Other Contexts
{#suspending-context-switching-to-other-contexts}
- to [blocking context](#blocking-context): [`blockingContext()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) - enables `ProgressManager.checkCanceled()`, forwards modality state, etc. This function has an opposite behavior to [`runBlockingCancellable()`](#blocking-context-switching-to-other-contexts).
- to [progress indicator](#progress-indicator): unavailable*
*_[`coroutineToIndicator()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) is an internal API and exists only to aid platform migration_
## Blocking Context
Executing tasks in a blocking context means executing them on a thread without access to the [coroutine context](#suspending-context) (basically, in non-suspending functions) and not under [a progress indicator](#progress-indicator).
Executing tasks in a blocking context means executing them on a thread without access to the [coroutine context](#suspending-context-coroutines) (basically, in non-suspending functions) and not under [a progress indicator](#progress-indicator).
Such tasks can't be canceled by using coroutines' or progress indicator's cancellation capabilities, which may block threads and consume resources even if the task is no longer relevant.
Usually, plugins should not execute new code in the blocking context.
Always prefer executing tasks in the [suspending context](#suspending-context) or under the [progress indicator](#progress-indicator) if a plugin cannot use Kotlin.
Always prefer executing tasks in the [suspending context](#suspending-context-coroutines) or under the [progress indicator](#progress-indicator) if a plugin cannot use Kotlin.
> Functions which schedule execution via [`Application.executeOnPooledThread()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/application/Application.java)
> and similar methods, and which rely on [`ProgressManager.checkCanceled()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java)
@ -89,24 +47,6 @@ Always prefer executing tasks in the [suspending context](#suspending-context) o
>
> Inspection <control>Plugin DevKit | Code | Calling method should be annotated with @RequiresBlockingContext</control> reports missing annotations.
### Cancellation Check
{#blocking-context-cancellation-check}
- [`ProgressManager.checkCanceled()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java)
### Progress Reporting
{#blocking-context-progress-reporting}
- unavailable
### Switching to Other Contexts
{#blocking-context-switching-to-other-contexts}
- to [suspending context](#suspending-context): [`runBlockingCancellable()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt). This function has an opposite behavior to [`blockingContext()`](#suspending-context-switching-to-other-contexts).
- to [progress indicator](#progress-indicator): unavailable*<br/>
*_[`blockingContextToIndicator()`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) is an internal API used only to aid platform migration_
## Progress Indicator
<primary-label ref="obsolete-2024.1"/>
@ -124,18 +64,125 @@ See the [](background_processes.md#progress-api) section for details.
>
{style="tip" title="Obsolete approach since 2024.1"}
### Cancellation Check
{#progress-indicator-cancellation-check}
## Execution Contexts APIs
- `ProgressManager.checkCanceled()` - as described in the [Background Processes: Cancellation](background_processes.md#cancellation) section
### Cancellation Check
The following table presents APIs to use for checking whether a task was canceled in different execution contexts.
<table style="header-column">
<tr>
<td width="16%">Suspending</td>
<td>
<code><a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-active.html">ensureActive()</a></code> from Kotlin coroutine's API
<warning>
Note that <code><a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java">ProgressManager.checkCanceled()</a></code> does not work in a suspending context.
</warning>
</td>
</tr>
<tr>
<td>Blocking</td>
<td>
<code><a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java">ProgressManager.checkCanceled()</a></code>
<p>See <a href="background_processes.md#cancellation">Background Processes: Cancellation</a> for details.</p>
</td>
</tr>
<tr>
<td>Progress&nbsp;Indicator</td>
<td>
<code><a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressIndicator.java">ProgressIndicator.checkCanceled()</a></code>, <code><a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java">ProgressManager.checkCanceled()</a></code>
<p>See <a href="background_processes.md#cancellation">Background Processes: Cancellation</a> for details.</p>
</td>
</tr>
</table>
### Progress Reporting
{#progress-indicator-progress-reporting}
- [`ProgressIndicator`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressIndicator.java) or [`ProgressManager`'s](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) methods as described in [Background Processes: Tracking Progress](background_processes.md#tracking-progress)
The following table presents the possibilities and APIs to use for reporting progress in different execution contexts.
### Switching to Other Contexts
{#progress-indicator-switching-to-other-contexts}
<table style="header-column">
<tr>
<td width="16%">Suspending</td>
<td>
<ul>
<li><code><a href="%gh-ic%/platform/util/progress/src/impl/ProgressStep.kt">ProgressStep</a></code> - a step-based progress reporting (see its KDoc for details)</li>
<li><code><a href="%gh-ic%/platform/util/progress/src/RawProgressReporter.kt">RawProgressReporter</a></code> - a raw text, details, and fraction reporting (invoked via <code><a href="%gh-ic%/platform/util/progress/src/steps.kt">reportRawProgress()</a></code>)</li>
<li></li>
</ul>
<p>
Any <code><a href="%gh-ic%/platform/util/progress/src/steps.kt">report*Progress()</a></code> function must be used inside <code>withBackgroundProgress()</code>, <code>withModalProgress()</code>, or <code>runWithModalProgressBlocking()</code> from <a href="%gh-ic%/platform/progress/shared/src/tasks.kt">tasks.kt</a>.
Otherwise, if there is no reporter in the context, using `report*Progress()` will have no effect.
Example:
</p>
<code-block lang="kotlin">
withBackgroundProgress(...) { // or other
// ...
reportProgress { reporter -> // or another report*Progress
// do tasks and report progress
}
// ...
}
</code-block>
</td>
</tr>
<tr>
<td>Blocking</td>
<td>
unavailable
</td>
</tr>
<tr>
<td>Progress&nbsp;Indicator</td>
<td>
<a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressIndicator.java"><code>ProgressIndicator</code>'s</a> or <a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java"><code>ProgressManager</code>'s</a> methods
<p>See <a href="background_processes.md#tracking-progress">Background Processes: Tracking Progress</a> for details.</p>
</td>
</tr>
</table>
- to [suspending context](#suspending-context): [`runBlockingCancellable`](%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt)
- to [blocking context](#blocking-context): unavailable
### Switching Between Contexts
The following table presents the possibilities and APIs to use for switching between different execution contexts.
<table style="both">
<tr>
<td width="16%"></td>
<td>To&nbsp;Suspending</td>
<td>To&nbsp;Blocking</td>
<td>To&nbsp;Progress&nbsp;Indicator</td>
</tr>
<tr>
<td>From Suspending</td>
<td>-</td>
<td><code>blockingContext()</code> <sup>1</sup></td>
<td>unavailable <sup>3</sup></td>
</tr>
<tr>
<td>From Blocking</td>
<td><code>runBlockingCancellable()</code> <sup>2</sup></td>
<td>-</td>
<td>unavailable <sup>4</sup></td>
</tr>
<tr>
<td>From Progress&nbsp;Indicator</td>
<td><code>runBlockingCancellable()</code> <sup>2</sup></td>
<td>unavailable</td>
<td>-</td>
</tr>
<tr>
<!--td><i>Notes</i></td-->
<td colspan="4">
<p><sup>1</sup> <i><code>blockingContext()</code> enables <code>ProgressManager.checkCanceled()</code>, forwards modality state, etc.</i></p>
<p><sup>2</sup> <i><code>runBlockingCancellable()</code> has an opposite behavior to <code>blockingContext()</code></i></p>
<p><sup>3</sup> <i><a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt"><code>coroutineToIndicator()</code></a> is an internal API to aid platform migration</i></p>
<p><sup>4</sup> <i><a href="%gh-ic%/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt"><code>blockingContextToIndicator()</code></a> is an internal API to aid platform migration</i></p>
</td>
</tr>
</table>
It is only possible to:
- switch from the blocking context or progress indicator to the suspending context
- switch from the suspending context to the blocking context
The lack of an API for switching from suspending and blocking contexts to progress indicator is intentional.
Cancellable and trackable tasks should be run in coroutines as the progress indicator is obsolete since 2024.1.