diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt index ea3feaf8b368..98b5d91a32d8 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt @@ -30,6 +30,8 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.concurrency.Promise import org.jetbrains.concurrency.asPromise import java.util.concurrent.CompletableFuture +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext @ApiStatus.Internal class FrontendXValue private constructor( @@ -368,9 +370,9 @@ private fun renderAdvancedPresentation(renderer: XValuePresentation.XValueTextRe } } -internal fun Obsolescent.childCoroutineScope(parentScope: CoroutineScope, name: String): CoroutineScope { +internal fun Obsolescent.childCoroutineScope(parentScope: CoroutineScope, name: String, context: CoroutineContext = EmptyCoroutineContext): CoroutineScope { val obsolescent = this - val scope = parentScope.childScope(name) + val scope = parentScope.childScope(name, context) parentScope.launch(context = Dispatchers.IO, start = CoroutineStart.UNDISPATCHED) { while (!obsolescent.isObsolete) { delay(ConcurrencyUtil.DEFAULT_TIMEOUT_MS) diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt index 4b1a86ed92c1..8baf9a3f4e80 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.project.Project import com.intellij.platform.debugger.impl.frontend.frame.VariablesPreloadManager import com.intellij.platform.debugger.impl.rpc.* import com.intellij.platform.debugger.impl.shared.XValuesPresentationBuilder -import com.intellij.platform.util.coroutines.childScope import com.intellij.ui.SimpleTextAttributes import com.intellij.xdebugger.frame.XCompositeNode import com.intellij.xdebugger.frame.XNamedValue @@ -58,8 +57,8 @@ internal class FrontendXValueContainer( override fun computeChildren(node: XCompositeNode) { val childrenManager = getOrCreteChildrenManager(node) - val scope = cs.childScope("FrontendXValueContainer#computeChildren", childrenManager) - node.childCoroutineScope(parentScope = cs, "FrontendXValueContainer#computeChildren").launch(Dispatchers.EDT) { + val scope = node.childCoroutineScope(parentScope = cs, "FrontendXValueContainer#computeChildren", childrenManager) + scope.launch(Dispatchers.EDT) { val flow = childrenManager.getChildrenEventsFlow(id) val builder = XValuesPresentationBuilder() flow.collect { event -> diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt index 0694e0e8c8fc..aa89849a1251 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt @@ -3,11 +3,10 @@ package com.intellij.platform.debugger.impl.frontend.frame import com.intellij.openapi.diagnostic.fileLogger import com.intellij.platform.debugger.impl.rpc.* -import com.intellij.platform.util.coroutines.childScope import com.intellij.util.AwaitCancellationAndInvoke +import com.intellij.util.awaitCancellationAndInvoke import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.flow.Flow @@ -29,19 +28,19 @@ class VariablesPreloadManager( treeState: XDebuggerTreeExpandedNode, frameId: XStackFrameId, ) { - private val cs = parentScope.childScope("VariablesPreloadManager") private val preloadedEvents = ConcurrentHashMap>() init { - markToBeLoaded(frameId) - cs.launch { + val localEvents = hashMapOf>() + markToBeLoaded(frameId, localEvents) + parentScope.launch { XValueApi.getInstance().computeExpandedChildren(frameId, treeState).collect { event -> when (event) { is PreloadChildrenEvent.ToBePreloaded -> { - markToBeLoaded(event.id) + markToBeLoaded(event.id, localEvents) } is PreloadChildrenEvent.ExpandedChildrenEvent -> { - val channel = preloadedEvents[event.id] ?: run { + val channel = localEvents[event.id] ?: run { fileLogger().error("Preloaded event for ${event.id} was not properly received") return@collect } @@ -50,22 +49,24 @@ class VariablesPreloadManager( } } } + parentScope.awaitCancellationAndInvoke { + preloadedEvents.clear() + } } fun getChildrenEventsFlow(entityId: XContainerId): Flow? { - val eventsChannel = preloadedEvents[entityId] ?: return null + // channel can be consumed only once, so we remove it here + val eventsChannel = preloadedEvents.remove(entityId) ?: return null return channelFlow { eventsChannel.consumeEach { send(it) } } } - private fun markToBeLoaded(id: XContainerId) { - val old = preloadedEvents.put(id, Channel(capacity = Channel.UNLIMITED)) + private fun markToBeLoaded(id: XContainerId, localEvents: MutableMap>) { + val channel = Channel(capacity = Channel.UNLIMITED) + val old = localEvents.put(id, channel) assert(old == null) { "Channel for $id was already registered" } - } - - internal fun cancel() { - cs.cancel() + preloadedEvents[id] = channel } companion object {