mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[terminal] IJPL-182482 Implement terminal backend typing latency
This process is not single-threaded, but it involves only one thread transfer: from the receiving coroutine to the terminal thread, which uses an executor that is never shut down, so it's pretty much a guarantee that for every byte array there will be at least an attempt to write it. Using this fact, we can store a byte array to activity map and remove activities when the byte arrays are written. Using a try-finally block there ensures that the activity is eventually finished. The activity ID is transferred along with the event, to ensure that the same ID is used on the frontend and on the backend. GitOrigin-RevId: 66587a5cca59053dab1e4bf2bee9bd098fe10637
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2681b905e6
commit
c082a207f0
@@ -15,7 +15,7 @@ data class TerminalResizeEvent(val newSize: TerminalSizeDto) : TerminalInputEven
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Serializable
|
||||
data class TerminalWriteBytesEvent(val bytes: ByteArray) : TerminalInputEvent {
|
||||
data class TerminalWriteBytesEvent(val bytes: ByteArray, val id: Int? = null) : TerminalInputEvent {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
@@ -96,8 +96,8 @@ class TerminalInput(
|
||||
}
|
||||
|
||||
fun sendBytes(data: ByteArray) {
|
||||
val writeBytesEvent = TerminalWriteBytesEvent(data)
|
||||
val fusActivity = ReworkedTerminalUsageCollector.getCurrentKeyEventTypingActivityOrNull()
|
||||
val writeBytesEvent = TerminalWriteBytesEvent(bytes = data, id = fusActivity?.id)
|
||||
fusActivity?.startTerminalInputEventProcessing(writeBytesEvent)
|
||||
sendEvent(writeBytesEvent)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.jetbrains.plugins.terminal.runner.LocalOptionsConfigurer;
|
||||
import org.jetbrains.plugins.terminal.runner.LocalShellIntegrationInjector;
|
||||
import org.jetbrains.plugins.terminal.runner.LocalTerminalStartCommandBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -188,6 +189,21 @@ public class LocalTerminalDirectRunner extends AbstractTerminalRunner<PtyProcess
|
||||
@Override
|
||||
public @NotNull TtyConnector createTtyConnector(@NotNull PtyProcess process) {
|
||||
return new PtyProcessTtyConnector(process, myDefaultCharset) {
|
||||
@Override
|
||||
public void write(byte[] bytes) throws IOException {
|
||||
var fusActivity = ReworkedTerminalUsageCollector.getBackendTypingActivityOrNull(bytes);
|
||||
try {
|
||||
super.write(bytes);
|
||||
if (fusActivity != null) {
|
||||
fusActivity.reportDuration();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (fusActivity != null) {
|
||||
fusActivity.finishBytesProcessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
+50
@@ -59,6 +59,12 @@ object ReworkedTerminalUsageCollector : CounterUsagesCollector() {
|
||||
DURATION_FIELD,
|
||||
)
|
||||
|
||||
private val backendTypingLatencyEvent = GROUP.registerVarargEvent(
|
||||
"terminal.backend.typing.latency",
|
||||
INPUT_EVENT_ID_FIELD,
|
||||
DURATION_FIELD,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun logLocalShellStarted(project: Project, shellCommand: Array<String>) {
|
||||
localShellStartedEvent.log(project,
|
||||
@@ -88,6 +94,13 @@ object ReworkedTerminalUsageCollector : CounterUsagesCollector() {
|
||||
)
|
||||
}
|
||||
|
||||
internal fun logBackendLatency(inputEventId: Int, duration: Duration) {
|
||||
backendTypingLatencyEvent.log(
|
||||
INPUT_EVENT_ID_FIELD with inputEventId,
|
||||
DURATION_FIELD with duration
|
||||
)
|
||||
}
|
||||
|
||||
fun startFrontendTypingActivity(e: KeyEvent): FrontendTypingActivity? {
|
||||
ThreadingAssertions.softAssertEventDispatchThread()
|
||||
if (e.id != KeyEvent.KEY_TYPED) return null
|
||||
@@ -104,6 +117,18 @@ object ReworkedTerminalUsageCollector : CounterUsagesCollector() {
|
||||
fun getFrontendTypingActivityOrNull(event: TerminalInputEvent): FrontendTypingActivity? {
|
||||
return frontendTypingActivityByInputEvent[InputEventIdentityWrapper(event)]
|
||||
}
|
||||
|
||||
fun tryStartBackendTypingActivity(event: TerminalWriteBytesEvent) {
|
||||
val id = event.id ?: return
|
||||
val bytes = event.bytes
|
||||
val activity = BackendTypingActivityImpl(id, bytes)
|
||||
backendTypingActivityByByteArray[bytes] = activity
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBackendTypingActivityOrNull(bytes: ByteArray): BackendTypingActivity? {
|
||||
return backendTypingActivityByByteArray[bytes]
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -115,10 +140,19 @@ interface FrontendTypingActivity {
|
||||
fun finishTerminalInputEventProcessing()
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface BackendTypingActivity {
|
||||
val id: Int
|
||||
fun reportDuration()
|
||||
fun finishBytesProcessing()
|
||||
}
|
||||
|
||||
private val frontendTypingActivityId = AtomicInteger()
|
||||
private var currentKeyEventTypingActivity: FrontendTypingActivityImpl? = null
|
||||
private val frontendTypingActivityByInputEvent = ConcurrentHashMap<InputEventIdentityWrapper, FrontendTypingActivityImpl>()
|
||||
|
||||
private val backendTypingActivityByByteArray = ConcurrentHashMap<ByteArray, BackendTypingActivityImpl>()
|
||||
|
||||
// TerminalWriteBytesEvent is a data class, but we need to track individual events, not their content
|
||||
private class InputEventIdentityWrapper(private val event: TerminalInputEvent) {
|
||||
override fun equals(other: Any?): Boolean = event === (other as? InputEventIdentityWrapper)?.event
|
||||
@@ -161,4 +195,20 @@ private class FrontendTypingActivityImpl(override val id: Int) : FrontendTypingA
|
||||
}
|
||||
}
|
||||
|
||||
private class BackendTypingActivityImpl(override val id: Int, private val bytes: ByteArray) : BackendTypingActivity {
|
||||
private val start = TimeSource.Monotonic.markNow()
|
||||
|
||||
override fun reportDuration() {
|
||||
val duration = start.elapsedNow()
|
||||
ReworkedTerminalUsageCollector.logBackendLatency(
|
||||
inputEventId = id,
|
||||
duration = duration,
|
||||
)
|
||||
}
|
||||
|
||||
override fun finishBytesProcessing() {
|
||||
backendTypingActivityByByteArray.remove(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = logger<FrontendTerminalSession>()
|
||||
|
||||
Reference in New Issue
Block a user