diff --git a/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt b/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt index 2bae63fbac09..1957eb202443 100644 --- a/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt +++ b/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt @@ -11,6 +11,7 @@ import com.intellij.execution.dashboard.RunDashboardServiceId import com.intellij.ide.ui.icons.rpcId import com.intellij.openapi.project.Project import com.intellij.platform.execution.dashboard.splitApi.* +import com.intellij.platform.util.coroutines.childScope import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.* @@ -28,6 +29,13 @@ internal class BackendRunDashboardManagerState(private val project: Project) { private val sharedExcludedTypes = MutableStateFlow(emptySet()) + private val sharedStateUpdatesQueue = BackendRunDashboardUpdatesQueue(RunDashboardCoroutineScopeProvider.getInstance(project) + .cs.childScope("Backend run dashboard shared state updates")) + + private fun scheduleSharedStateUpdate(update: SequentialComputationRequest) { + sharedStateUpdatesQueue.submit(update) + } + fun getLinkByServiceId(link: String, serviceId: RunDashboardServiceId): Runnable? { return tagCallbacksByServiceId[serviceId]?.firstOrNull { linkDto -> linkDto.presentableText == link }?.callback } @@ -41,21 +49,27 @@ internal class BackendRunDashboardManagerState(private val project: Project) { } fun setServices(value: List>) { - val flattenServices = value.flatten() - sharedServicesState.value = flattenServices.map { backendServiceModel -> - createServiceDto(backendServiceModel) - } + scheduleSharedStateUpdate { + val flattenServices = value.flatten() + sharedServicesState.value = flattenServices.map { backendServiceModel -> + createServiceDto(backendServiceModel) + } - val effectiveServicesSet = flattenServices.asSequence().map { it.uuid }.toSet() - tagCallbacksByServiceId.keys.retainAll(effectiveServicesSet) + val effectiveServicesSet = flattenServices.asSequence().map { it.uuid }.toSet() + tagCallbacksByServiceId.keys.retainAll(effectiveServicesSet) + } } fun setSettings(openRunningConfigInTab: Boolean) { - sharedSettings.value = RunDashboardSettingsDto(openRunningConfigInTab) + scheduleSharedStateUpdate { + sharedSettings.value = RunDashboardSettingsDto(openRunningConfigInTab) + } } fun fireExcludedTypesUpdated(excludedTypes: Set) { - sharedExcludedTypes.value = excludedTypes + scheduleSharedStateUpdate { + sharedExcludedTypes.value = excludedTypes + } } fun getExcludedTypes(): Flow> { @@ -63,11 +77,13 @@ internal class BackendRunDashboardManagerState(private val project: Project) { } fun fireStatusUpdated(backendService: RunDashboardService, persistedStatus: RunDashboardRunConfigurationStatus?) { - val effectiveStatus = when { - backendService.descriptor == null && persistedStatus != null -> persistedStatus - else -> RunDashboardRunConfigurationStatus.getStatus(backendService.descriptor) + scheduleSharedStateUpdate { + val effectiveStatus = when { + backendService.descriptor == null && persistedStatus != null -> persistedStatus + else -> RunDashboardRunConfigurationStatus.getStatus(backendService.descriptor) + } + sharedStatuses.tryEmit(ServiceStatusDto(backendService.uuid, effectiveStatus.id)) } - sharedStatuses.tryEmit(ServiceStatusDto(backendService.uuid, effectiveStatus.id)) } fun getStatuses(): Flow { @@ -75,9 +91,11 @@ internal class BackendRunDashboardManagerState(private val project: Project) { } fun fireCustomizationUpdated(backendService: RunDashboardService, customizers: List) { - val value = createCustomizationDto(backendService, customizers) - tagCallbacksByServiceId[backendService.uuid] = value.links - sharedServicesCustomizations.tryEmit(value) + scheduleSharedStateUpdate { + val value = createCustomizationDto(backendService, customizers) + tagCallbacksByServiceId[backendService.uuid] = value.links + sharedServicesCustomizations.tryEmit(value) + } } fun getCustomizations(): Flow { @@ -96,7 +114,9 @@ internal class BackendRunDashboardManagerState(private val project: Project) { } fun setConfigurationTypes(value: Set) { - sharedConfigurationTypes.value = value + scheduleSharedStateUpdate { + sharedConfigurationTypes.value = value + } } fun getConfigurationTypes(): Flow> { diff --git a/platform/execution.dashboard/src/BackendRunDashboardUpdatesQueue.kt b/platform/execution.dashboard/src/BackendRunDashboardUpdatesQueue.kt new file mode 100644 index 000000000000..a9068f2f3d72 --- /dev/null +++ b/platform/execution.dashboard/src/BackendRunDashboardUpdatesQueue.kt @@ -0,0 +1,28 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.platform.execution.dashboard + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch + +internal typealias SequentialComputationRequest = suspend () -> Unit + +internal class BackendRunDashboardUpdatesQueue(scope: CoroutineScope) { + private val channel = Channel(Channel.UNLIMITED) + + init { + scope.launch { + for (request in channel) { + request() + } + } + } + + fun submit(block: SequentialComputationRequest) { + channel.trySend(block) + } + + fun close() { + channel.close() + } +} \ No newline at end of file