[threading] IJPL-178593: Stabilize Dispatchers.UI and backgroundWriteAction

GitOrigin-RevId: a0922de16d4a71f03d0f13be145de707db6f09f7
This commit is contained in:
Konstantin Nisht
2025-10-13 13:34:16 +00:00
committed by intellij-monorepo-bot
parent f3161c5a38
commit 5c9d08a0c2
4 changed files with 56 additions and 36 deletions
@@ -210,13 +210,8 @@ com.intellij.openapi.application.Application
- *:isWriteThread():Z
- *:runWriteIntentReadAction(com.intellij.openapi.util.ThrowableComputable):java.lang.Object
f:com.intellij.openapi.application.CoroutinesKt
- *sf:backgroundWriteAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- *sf:getEdtImmediate(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- *sf:getUI(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- *sf:getUiImmediate(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- *sf:getUiWithModelAccess(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- *sf:getUiWithModelAccessImmediate(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- *sf:readAndBackgroundWriteAction(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- *sf:readAndBackgroundWriteActionUndispatched(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- *sf:readAndEdtWriteActionUndispatched(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- *sf:writeAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
-10
View File
@@ -604,17 +604,7 @@ f:com.intellij.openapi.application.ApplicationNamesInfo
- getScriptName():java.lang.String
f:com.intellij.openapi.application.CoroutinesKt
- bsf:asContextElement(com.intellij.openapi.application.ModalityState):kotlin.coroutines.CoroutineContext
- sf:constrainedReadAction(com.intellij.openapi.application.ReadConstraint[],kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:constrainedReadActionBlocking(com.intellij.openapi.application.ReadConstraint[],kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:constrainedReadAndWriteAction(com.intellij.openapi.application.ReadConstraint[],kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- sf:edtWriteAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:getEDT(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- sf:readAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:readActionBlocking(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:readAndEdtWriteAction(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- sf:readAndWriteAction(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- sf:smartReadAction(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:smartReadActionBlocking(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
com.intellij.openapi.application.DumbAwareSearchParameters
- com.intellij.util.QueryParameters
- a:getProject():com.intellij.openapi.project.Project
+14
View File
@@ -449,6 +449,20 @@ com.intellij.openapi.application.BaseExpirableExecutor
- a:expireWith(com.intellij.openapi.Disposable):com.intellij.openapi.application.BaseExpirableExecutor
- a:submit(java.lang.Runnable):org.jetbrains.concurrency.CancellablePromise
- a:submit(java.util.concurrent.Callable):org.jetbrains.concurrency.CancellablePromise
f:com.intellij.openapi.application.CoroutinesKt
- sf:backgroundWriteAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:constrainedReadAction(com.intellij.openapi.application.ReadConstraint[],kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:constrainedReadActionBlocking(com.intellij.openapi.application.ReadConstraint[],kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:constrainedReadAndWriteAction(com.intellij.openapi.application.ReadConstraint[],kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- sf:edtWriteAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:getEDT(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- sf:getUI(kotlinx.coroutines.Dispatchers):kotlin.coroutines.CoroutineContext
- sf:readAction(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:readActionBlocking(kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:readAndBackgroundWriteAction(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- sf:readAndEdtWriteAction(kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
- sf:smartReadAction(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
- sf:smartReadActionBlocking(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object
com.intellij.openapi.application.ModalityStateListener
- java.util.EventListener
- beforeModalityStateChanged(Z,java.lang.Object):V
@@ -161,7 +161,6 @@ suspend fun <T> readAndWriteAction(action: ReadAndWriteScope.() -> ReadResult<T>
/**
* Same as [readAndEdtWriteAction], but invokes write actions on a background thread instead of EDT.
*/
@Experimental
suspend fun <T> readAndBackgroundWriteAction(action: ReadAndWriteScope.() -> ReadResult<T>): T {
return readWriteActionSupport().executeReadAndWriteAction(emptyArray(), false, false, action)
}
@@ -293,19 +292,13 @@ suspend fun <T> writeAction(action: () -> T): T {
* Runs given [action] under [write lock][com.intellij.openapi.application.Application.runWriteAction].
*
* This function dispatches the [action] by [Dispatchers.Default] within the [context modality state][asContextElement].
* Acquiring the write-lock happens in blocking manner,
* i.e. [runWriteAction][com.intellij.openapi.application.Application.runWriteAction] call will block
* until all currently running read actions are finished.
* The lock is acquired in suspending manner, so the calling coroutine will be suspended during the acquisition.
*
* NB This function is an API stub.
* The implementation will change once running write actions would be allowed on other threads.
* This function exists to make it possible to use it in suspending contexts
* before the platform is ready to handle write actions differently.
* A pending background write action can be diagnosed by an inspection of _coroutine dumps_.
*
* @see readAndBackgroundWriteAction
* @see com.intellij.openapi.command.writeCommandAction
*/
@Experimental
suspend fun <T> backgroundWriteAction(action: () -> T): T {
return readWriteActionSupport().runWriteAction(action)
}
@@ -343,12 +336,27 @@ private fun readWriteActionSupport() = ApplicationManager.getApplication().getSe
fun ModalityState.asContextElement(): CoroutineContext = asContextElement()
/**
* UI dispatcher which dispatches onto Swing event dispatching thread within the [context modality state][asContextElement].
* The computations scheduled by this dispatcher **are protected** by the Write-Intent lock, and they are allowed to upgrade to write actions.
* A coroutine dispatcher that executes coroutines on the AWT Event Dispatching Thread
* within the [context modality state][asContextElement] and with write-intent lock acquired.
*
* Use-cases:
* - Use this dispatcher if you are working with the legacy IntelliJ Platform model code (PSI, VFS, Documents) which requires model access on EDT.
* - Use [Dispatchers.UI] if you are working with the UI and you do not touch the IntelliJ Platform model.
*
* ### Locking Behavior
* This dispatcher is different from [Dispatchers.UI] in the aspect of handling the Read/Write lock:
* the computations scheduled by this dispatcher **are protected by the Write-Intent lock** (see [Application]),
* hence they are allowed to acquire write actions atomically.
*
* ### Ordering Guarantees
* This dispatcher is fair: two `launch(Dispatchers.EDT)` are executed in the order of their scheduling.
*
* _NB:_ There are no ordering guarantees between `launch(Dispatchers.EDT)` and `launch(Dispatchers.UI)` coroutines:
* when scheduled sequentially, either one can execute first.
*
* ### Modality Behavior
*
* If no context modality state is specified, then the coroutine is dispatched within [ModalityState.nonModal] modality state.
*
* Prefer [Dispatchers.UI] for computations on EDT.
*/
@Suppress("UnusedReceiverParameter")
val Dispatchers.EDT: CoroutineContext get() = coroutineSupport().uiDispatcher(UiDispatcherKind.LEGACY, false)
@@ -367,15 +375,28 @@ val Dispatchers.EDT: CoroutineContext get() = coroutineSupport().uiDispatcher(Ui
fun Dispatchers.ui(kind: UiDispatcherKind = UiDispatcherKind.STRICT, immediate: Boolean = false): CoroutineContext = coroutineSupport().uiDispatcher(kind, immediate)
/**
* UI dispatcher which dispatches onto Swing event dispatching thread within the [context modality state][asContextElement].
* The computations scheduled by this dispatcher are **not** protected by any lock, and it is forbidden to initiate Read or Write actions.
* A coroutine dispatcher that executes coroutines on the AWT Event Dispatching Thread
* within the [context modality state][asContextElement] and without write-intent lock acquired.
*
* Use-cases:
* - Use this dispatcher if you are working with the UI and you do not touch the IntelliJ Platform model.
* - Use [Dispatchers.EDT] if you are working with the IntelliJ Platform model on EDT.
*
* ### Locking Behavior
* This dispatcher is different from [Dispatchers.EDT] in the aspect of handling the Read/Write lock:
* the computations scheduled by this dispatcher **are not protected by the Write-Intent lock** (see [Application]),
* and it is forbidden to initiate read or write actions inside.
*
* ### Ordering Guarantees
* This dispatcher is fair: two `launch(Dispatchers.UI)` are executed in the order of their scheduling.
*
* _NB:_ There are no ordering guarantees between `launch(Dispatchers.EDT)` and `launch(Dispatchers.UI)` coroutines:
* when scheduled sequentially, either one can execute first.
*
* ### Modality Behavior
*
* If no context modality state is specified, then the coroutine is dispatched within [ModalityState.nonModal] modality state.
*
* Use [Dispatchers.UI] when in doubt, use [Dispatchers.Main] if the coroutine doesn't care about IntelliJ Platform model (PSI, VFS, etc.),
* e.g., when it can be executed outside of IJ process.
*/
@get:Experimental
@Suppress("UnusedReceiverParameter")
val Dispatchers.UI: CoroutineContext get() = coroutineSupport().uiDispatcher(kind = UiDispatcherKind.STRICT, immediate = false)
@@ -385,7 +406,7 @@ val Dispatchers.UI: CoroutineContext get() = coroutineSupport().uiDispatcher(kin
*
* If no context modality state is specified, then the coroutine is dispatched within [ModalityState.nonModal] modality state.
*/
@get:Experimental
@get:Internal
@Suppress("UnusedReceiverParameter")
val Dispatchers.UiWithModelAccess: CoroutineContext get() = coroutineSupport().uiDispatcher(kind = UiDispatcherKind.RELAX, immediate = false)
@@ -407,7 +428,7 @@ val Dispatchers.UiImmediate: CoroutineContext get() = coroutineSupport().uiDispa
* The version of [Dispatchers.UiWithModelAccess] which has properties of [MainCoroutineDispatcher.immediate]
*/
@Suppress("UnusedReceiverParameter")
@get:Experimental
@get:Internal
val Dispatchers.UiWithModelAccessImmediate: CoroutineContext get() = coroutineSupport().uiDispatcher(kind = UiDispatcherKind.RELAX, immediate = true)
private fun coroutineSupport() = ApplicationManager.getApplication().getService(CoroutineSupport::class.java)