databatcher

desktopmerge
filip mertens 1 year ago
parent 1ab6a5219d
commit 4e84e698cd
  1. 57
      apps/remixdesktop/src/lib/databatcher.ts
  2. 11
      apps/remixdesktop/src/plugins/xtermPlugin.ts
  3. 8
      libs/remix-ui/xterm/src/lib/components/remix-ui-xterm.tsx

@ -0,0 +1,57 @@
import {EventEmitter} from 'events';
import { StringDecoder } from 'string_decoder';
// Max duration to batch session data before sending it to the renderer process.
const BATCH_DURATION_MS = 16;
// Max size of a session data batch. Note that this value can be exceeded by ~4k
// (chunk sizes seem to be 4k at the most)
const BATCH_MAX_SIZE = 200 * 1024;
// Data coming from the pty is sent to the renderer process for further
// vt parsing and rendering. This class batches data to minimize the number of
// IPC calls. It also reduces GC pressure and CPU cost: each chunk is prefixed
// with the window ID which is then stripped on the renderer process and this
// overhead is reduced with batching.
export class DataBatcher extends EventEmitter {
uid: number;
decoder: StringDecoder;
data!: string;
timeout!: NodeJS.Timeout | null;
constructor(uid: number) {
super();
this.uid = uid;
this.decoder = new StringDecoder('utf8');
this.reset();
}
reset() {
this.data = '';
this.timeout = null;
}
write(chunk: Buffer) {
if (this.data.length + chunk.length >= BATCH_MAX_SIZE) {
// We've reached the max batch size. Flush it and start another one
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.flush();
}
this.data += this.decoder.write(chunk);
if (!this.timeout) {
this.timeout = setTimeout(() => this.flush(), BATCH_DURATION_MS);
}
}
flush() {
// Reset before emitting to allow for potential reentrancy
const data = this.data;
this.reset();
this.emit('flush', data, this.uid);
}
}

@ -12,6 +12,7 @@ import {userInfo} from 'node:os'
import {findExecutable} from '../utils/findExecutable' import {findExecutable} from '../utils/findExecutable'
import {spawnSync} from 'child_process' import {spawnSync} from 'child_process'
import { stripAnsi } from '../lib' import { stripAnsi } from '../lib'
import { DataBatcher } from '../lib/databatcher'
export const detectDefaultShell = () => { export const detectDefaultShell = () => {
const {env} = process const {env} = process
@ -55,7 +56,6 @@ const parseEnv = (env: any) => {
.split('\n') .split('\n')
.filter((l) => Boolean(l))) { .filter((l) => Boolean(l))) {
const [key, ...values] = line.split('=') const [key, ...values] = line.split('=')
console.log(key, values)
Object.assign(returnValue, { Object.assign(returnValue, {
[key]: values.join('='), [key]: values.join('='),
}) })
@ -102,6 +102,7 @@ const clientProfile: Profile = {
class XtermPluginClient extends ElectronBasePluginClient { class XtermPluginClient extends ElectronBasePluginClient {
terminals: pty.IPty[] = [] terminals: pty.IPty[] = []
dataBatchers: DataBatcher[] = []
constructor(webContentsId: number, profile: Profile) { constructor(webContentsId: number, profile: Profile) {
super(webContentsId, profile) super(webContentsId, profile)
this.onload(() => { this.onload(() => {
@ -147,9 +148,13 @@ class XtermPluginClient extends ElectronBasePluginClient {
cwd: path || process.cwd(), cwd: path || process.cwd(),
env: env, env: env,
}) })
const dataBatcher = new DataBatcher(ptyProcess.pid)
ptyProcess.onData((data: string) => { ptyProcess.onData((data: string) => {
this.sendData(data, ptyProcess.pid) dataBatcher.write(Buffer.from(data))
//this.sendData(data, ptyProcess.pid)
})
dataBatcher.on('flush', (data: string, uid: number) => {
this.sendData(data, uid)
}) })
this.terminals[ptyProcess.pid] = ptyProcess this.terminals[ptyProcess.pid] = ptyProcess

@ -36,10 +36,6 @@ const RemixUiXterm = (props: RemixUiXtermProps) => {
props.setTerminalRef(pid, xtermRef.current) props.setTerminalRef(pid, xtermRef.current)
}, [xtermRef.current]) }, [xtermRef.current])
const onKey = (event: {key: string; domEvent: KeyboardEvent}) => {
send(event.key, pid)
}
const onResize = (event: { cols: number; rows: number }) => { const onResize = (event: { cols: number; rows: number }) => {
resize(event, pid) resize(event, pid)
} }
@ -60,7 +56,9 @@ const RemixUiXterm = (props: RemixUiXtermProps) => {
onResize={onResize} onResize={onResize}
onRender={() => fitAddon.fit()} onRender={() => fitAddon.fit()}
ref={xtermRef} ref={xtermRef}
onKey={onKey} onData={(data) => {
send(data, pid)
}}
></Xterm> ></Xterm>
) )
} }

Loading…
Cancel
Save