From 1f79f484e5cfc4d283fc894527887c1269c67df8 Mon Sep 17 00:00:00 2001 From: FalsePattern Date: Thu, 27 Mar 2025 22:02:33 +0100 Subject: [PATCH] fix: Make EDT coroutine modality state explicit everywhere and fix deadlocks --- CHANGELOG.md | 1 + .../zigbrains/debugger/runner/base/ZigDebugRunnerBase.kt | 2 +- .../settings/ZigDebuggerToolchainConfigurableUi.kt | 7 ++++--- .../zigbrains/project/module/ZigModuleBuilder.kt | 3 ++- .../zigbrains/project/settings/ZigProjectSettingsPanel.kt | 2 +- .../project/steps/discovery/ZigStepDiscoveryService.kt | 3 ++- .../zigbrains/shared/coroutine/CoroutinesUtil.kt | 6 +++--- .../zigbrains/lsp/settings/ZLSSettingsPanel.kt | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9fc144..3234eb54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Changelog structure reference: - Project - File path browse buttons in zig run configurations didn't work + - Occasional GUI deadlocks - Zig - IPC wrapper wasn't passing exit code diff --git a/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/runner/base/ZigDebugRunnerBase.kt b/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/runner/base/ZigDebugRunnerBase.kt index ccb53975..7f176753 100644 --- a/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/runner/base/ZigDebugRunnerBase.kt +++ b/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/runner/base/ZigDebugRunnerBase.kt @@ -93,7 +93,7 @@ abstract class ZigDebugRunnerBase> : ZigProgra } } } - return@reportProgress runInterruptibleEDT { + return@reportProgress runInterruptibleEDT(ModalityState.any()) { val debuggerManager = XDebuggerManager.getInstance(environment.project) debuggerManager.startSession(environment, object: XDebugProcessStarter() { override fun start(session: XDebugSession): XDebugProcess { diff --git a/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/settings/ZigDebuggerToolchainConfigurableUi.kt b/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/settings/ZigDebuggerToolchainConfigurableUi.kt index a9a79879..66b534f9 100644 --- a/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/settings/ZigDebuggerToolchainConfigurableUi.kt +++ b/cidr/src/main/kotlin/com/falsepattern/zigbrains/debugger/settings/ZigDebuggerToolchainConfigurableUi.kt @@ -31,6 +31,7 @@ import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.intellij.ide.plugins.PluginManager +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.observable.util.whenItemSelected import com.intellij.openapi.ui.ComboBox @@ -88,7 +89,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent { row(ZigDebugBundle.message("settings.debugger.toolchain.debugger.label")) { comment = cell(debuggerKindComboBox) .comment("", DEFAULT_COMMENT_WIDTH) { - zigCoroutineScope.launchWithEDT { + zigCoroutineScope.launchWithEDT(ModalityState.any()) { withModalProgress(ModalTaskOwner.component(debuggerKindComboBox), "Downloading debugger", TaskCancellation.cancellable()) { downloadDebugger() } @@ -96,7 +97,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent { } .applyToComponent { whenItemSelected(null) { - zigCoroutineScope.launchWithEDT { + zigCoroutineScope.launchWithEDT(ModalityState.any()) { this@ZigDebuggerToolchainConfigurableUi.update() } } @@ -111,7 +112,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent { cell(useClion) } } - zigCoroutineScope.launchWithEDT { + zigCoroutineScope.launchWithEDT(ModalityState.any()) { update() } } diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/module/ZigModuleBuilder.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/module/ZigModuleBuilder.kt index 8813878c..cb3ea5ab 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/module/ZigModuleBuilder.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/module/ZigModuleBuilder.kt @@ -29,6 +29,7 @@ import com.intellij.ide.util.projectWizard.ModuleBuilder import com.intellij.ide.util.projectWizard.ModuleWizardStep import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.module.ModuleType import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.util.Disposer @@ -58,7 +59,7 @@ class ZigModuleBuilder: ModuleBuilder() { val root = contentEntry.file ?: return val config = configurationData ?: return config.generateProject(this, rootModel.project, root, forceGitignore) - withEDTContext { + withEDTContext(ModalityState.defaultModalityState()) { root.refresh(false, true) } } diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsPanel.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsPanel.kt index eb3e71c9..cf9b83e6 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsPanel.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsPanel.kt @@ -95,7 +95,7 @@ class ZigProjectSettingsPanel(private val holder: ZigProjectConfigurationProvide } private fun dispatchAutodetect(force: Boolean) { - project.zigCoroutineScope.launchWithEDT { + project.zigCoroutineScope.launchWithEDT(ModalityState.any()) { withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) { autodetect(force) } diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/steps/discovery/ZigStepDiscoveryService.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/steps/discovery/ZigStepDiscoveryService.kt index e80c17dc..320118ec 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/steps/discovery/ZigStepDiscoveryService.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/steps/discovery/ZigStepDiscoveryService.kt @@ -27,6 +27,7 @@ import com.falsepattern.zigbrains.project.steps.discovery.ZigStepDiscoveryListen import com.falsepattern.zigbrains.shared.coroutine.withEDTContext import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger @@ -124,7 +125,7 @@ class ZigStepDiscoveryService(private val project: Project) { } private suspend fun dispatchReload() { - withEDTContext { + withEDTContext(ModalityState.defaultModalityState()) { FileDocumentManager.getInstance().saveAllDocuments() } doReload() diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt index ca9b6007..cc69c88a 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt @@ -39,7 +39,7 @@ inline fun runModalOrBlocking(taskOwnerFactory: () -> ModalTaskOwner, titleF } } -suspend inline fun withEDTContext(state: ModalityState = ModalityState.defaultModalityState(), noinline block: suspend CoroutineScope.() -> T): T { +suspend inline fun withEDTContext(state: ModalityState, noinline block: suspend CoroutineScope.() -> T): T { return withContext(Dispatchers.EDT + state.asContextElement(), block = block) } @@ -49,10 +49,10 @@ suspend inline fun withCurrentEDTModalityContext(noinline block: suspend Cor } } -suspend inline fun runInterruptibleEDT(state: ModalityState = ModalityState.defaultModalityState(), noinline targetAction: () -> T): T { +suspend inline fun runInterruptibleEDT(state: ModalityState, noinline targetAction: () -> T): T { return runInterruptible(Dispatchers.EDT + state.asContextElement(), block = targetAction) } -fun CoroutineScope.launchWithEDT(state: ModalityState = ModalityState.defaultModalityState(), block: suspend CoroutineScope.() -> Unit): Job { +fun CoroutineScope.launchWithEDT(state: ModalityState, block: suspend CoroutineScope.() -> Unit): Job { return launch(Dispatchers.EDT + state.asContextElement(), block = block) } \ No newline at end of file diff --git a/lsp/src/main/kotlin/com/falsepattern/zigbrains/lsp/settings/ZLSSettingsPanel.kt b/lsp/src/main/kotlin/com/falsepattern/zigbrains/lsp/settings/ZLSSettingsPanel.kt index 68f6c48f..0ad515ea 100644 --- a/lsp/src/main/kotlin/com/falsepattern/zigbrains/lsp/settings/ZLSSettingsPanel.kt +++ b/lsp/src/main/kotlin/com/falsepattern/zigbrains/lsp/settings/ZLSSettingsPanel.kt @@ -279,7 +279,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr } private fun dispatchAutodetect(force: Boolean) { - project.zigCoroutineScope.launchWithEDT { + project.zigCoroutineScope.launchWithEDT(ModalityState.any()) { withModalProgress(ModalTaskOwner.component(zlsPath), "Detecting ZLS...", TaskCancellation.cancellable()) { autodetect(force) }