From 1ae8a2eb0daa470cc4e805eae65ecbc999dfe6e9 Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 14 Apr 2025 15:13:15 +0300 Subject: [PATCH] [terminal] IJPL-182482 Reimplement backend typing latency measurement Pass event time and id to the TerminalStarter. Implement the separate method for that in our override for that. This way is more explicit and safe because it is not using the global state and matching by event object. GitOrigin-RevId: ddbf4373bf6226c72bd8db3bc0d8b849d4ba3112 --- .../terminal/backend/BackendTerminalInput.kt | 3 +- .../backend/StopAwareTerminalStarter.kt | 25 ------- .../terminal/backend/TerminalOutput.kt | 2 +- .../terminal/backend/TerminalSessionStart.kt | 4 +- .../terminal/backend/TerminalStarterEx.kt | 68 +++++++++++++++++++ .../fus/ReworkedTerminalUsageCollector.kt | 2 +- 6 files changed, 74 insertions(+), 30 deletions(-) delete mode 100644 plugins/terminal/backend/src/com/intellij/terminal/backend/StopAwareTerminalStarter.kt create mode 100644 plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalStarterEx.kt diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt index 38f190b89d43..eb0e3a77121d 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt @@ -12,6 +12,7 @@ import org.jetbrains.plugins.terminal.block.ui.withLock import org.jetbrains.plugins.terminal.util.STOP_EMULATOR_TIMEOUT import org.jetbrains.plugins.terminal.util.waitFor import java.util.concurrent.CancellationException +import kotlin.time.TimeSource internal fun createTerminalInputChannel( services: JediTermServices, @@ -50,7 +51,7 @@ private fun handleInputEvent(event: TerminalInputEvent, services: JediTermServic when (event) { is TerminalWriteBytesEvent -> { - terminalStarter.sendBytes(event.bytes, false) + terminalStarter.sendTrackedBytes(event.bytes, event.id, eventTime = TimeSource.Monotonic.markNow()) } is TerminalResizeEvent -> { terminalStarter.postResize(event.newSize.toTermSize(), RequestOrigin.User) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/StopAwareTerminalStarter.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/StopAwareTerminalStarter.kt deleted file mode 100644 index 8a69b2d56ed6..000000000000 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/StopAwareTerminalStarter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.intellij.terminal.backend - -import com.jediterm.core.typeahead.TerminalTypeAheadManager -import com.jediterm.terminal.TerminalDataStream -import com.jediterm.terminal.TerminalExecutorServiceManager -import com.jediterm.terminal.TerminalStarter -import com.jediterm.terminal.TtyConnector -import com.jediterm.terminal.model.JediTerminal - -internal class StopAwareTerminalStarter( - terminal: JediTerminal, - ttyConnector: TtyConnector, - dataStream: TerminalDataStream, - typeAheadManager: TerminalTypeAheadManager, - executorServiceManager: TerminalExecutorServiceManager, -) : TerminalStarter(terminal, ttyConnector, dataStream, typeAheadManager, executorServiceManager) { - @Volatile - var isStopped: Boolean = false - private set - - override fun requestEmulatorStop() { - super.requestEmulatorStop() - isStopped = true - } -} diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt index 950039b354a7..f19f0c21147c 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt @@ -230,7 +230,7 @@ internal fun createTerminalOutputFlow( return outputFlow } -private fun ensureEmulationActive(starter: StopAwareTerminalStarter) { +private fun ensureEmulationActive(starter: TerminalStarterEx) { if (Thread.interrupted() || starter.isStopped) { throw CancellationException("Terminal emulation was stopped") } diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt index cdd4622c0698..a165ef00ccbc 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt @@ -104,7 +104,7 @@ private fun createJediTermServices( val controller = ObservableJediTerminal(terminalDisplay, textBuffer, styleState) val typeAheadManager = TerminalTypeAheadManager(JediTermTypeAheadModel(controller, textBuffer, settings)) val executorService = TerminalExecutorServiceManagerImpl() - val terminalStarter = StopAwareTerminalStarter( + val terminalStarter = TerminalStarterEx( controller, connector, TtyBasedArrayDataStream(connector), @@ -137,6 +137,6 @@ internal class JediTermServices( val terminalDisplay: TerminalDisplayImpl, val controller: ObservableJediTerminal, val executorService: TerminalExecutorServiceManager, - val terminalStarter: StopAwareTerminalStarter, + val terminalStarter: TerminalStarterEx, val ttyConnector: ObservableTtyConnector, ) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalStarterEx.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalStarterEx.kt new file mode 100644 index 000000000000..a0e1c8470939 --- /dev/null +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalStarterEx.kt @@ -0,0 +1,68 @@ +package com.intellij.terminal.backend + +import com.intellij.openapi.diagnostic.thisLogger +import com.jediterm.core.input.KeyEvent +import com.jediterm.core.typeahead.TerminalTypeAheadManager +import com.jediterm.terminal.TerminalDataStream +import com.jediterm.terminal.TerminalExecutorServiceManager +import com.jediterm.terminal.TerminalStarter +import com.jediterm.terminal.TtyConnector +import com.jediterm.terminal.model.JediTerminal +import org.jetbrains.plugins.terminal.fus.ReworkedTerminalUsageCollector +import java.io.IOException +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.TimeMark + +internal class TerminalStarterEx( + terminal: JediTerminal, + private val ttyConnector: TtyConnector, + dataStream: TerminalDataStream, + typeAheadManager: TerminalTypeAheadManager, + executorServiceManager: TerminalExecutorServiceManager, +) : TerminalStarter(terminal, ttyConnector, dataStream, typeAheadManager, executorServiceManager) { + @Volatile + var isStopped: Boolean = false + private set + + private val singleThreadScheduledExecutor: ScheduledExecutorService = executorServiceManager.singleThreadScheduledExecutor + + @Volatile + private var isLastSentByteEscape = false + + override fun requestEmulatorStop() { + super.requestEmulatorStop() + isStopped = true + } + + /** + * Use for sending bytes for that typing latency should be reported. + * [eventTime] is the moment when this writing bytes event was initialized. + */ + fun sendTrackedBytes(bytes: ByteArray, eventId: Int, eventTime: TimeMark) { + val length = bytes.size + if (length > 0) { + isLastSentByteEscape = bytes[length - 1].toInt() == KeyEvent.VK_ESCAPE + } + execute { + try { + ttyConnector.write(bytes) + + val latency = eventTime.elapsedNow() + ReworkedTerminalUsageCollector.logBackendTypingLatency(eventId, latency) + } + catch (e: IOException) { + thisLogger().info("Cannot write to TtyConnector ${ttyConnector.javaClass.getName()}, connected: ${ttyConnector.isConnected}", e) + } + } + } + + override fun isLastSentByteEscape(): Boolean { + return isLastSentByteEscape || super.isLastSentByteEscape() + } + + private inline fun execute(crossinline action: () -> Unit) { + if (!singleThreadScheduledExecutor.isShutdown) { + singleThreadScheduledExecutor.execute { action() } + } + } +} diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/ReworkedTerminalUsageCollector.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/ReworkedTerminalUsageCollector.kt index 8435e925190a..2ce55b8dc6fc 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/ReworkedTerminalUsageCollector.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/ReworkedTerminalUsageCollector.kt @@ -99,7 +99,7 @@ object ReworkedTerminalUsageCollector : CounterUsagesCollector() { } @ApiStatus.Internal - fun logBackendLatency(inputEventId: Int, duration: Duration) { + fun logBackendTypingLatency(inputEventId: Int, duration: Duration) { backendTypingLatencyEvent.log( EVENT_ID_FIELD with inputEventId, DURATION_FIELD with duration