Terminal Architecture Comparison
Comparison of flow control mechanisms in modern terminals.Executive Summary
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ FLOW CONTROL COMPARISON │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Terminal Flow Control Type Render ACK Notes │
│ ────────── ────────────────── ────────── ───────────── │
│ Alacritty Lock-based X Mutex pattern │
│ Ghostty Queue-based X Fixed capacity │
│ xterm.js Buffer watermark X Parser callback │
│ WezTerm Time-based X Coalesce only │
│ Hyper Inherited X Via xterm.js │
│ MonoTerm ACK Gate O Consumer-driven │
│ │
└───────────────────────────────────────────────────────────────────────┘
Alacritty
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ ALACRITTY: Lock-Based Pattern │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Approach: │
│ │
│ Fair lock acquisition between threads │
│ Prevents starvation during high contention │
│ │
│ Characteristics: │
│ │
│ - No backpressure from renderer to PTY │
│ - No render completion signal │
│ - Fire-and-forget event model │
│ │
│ PTY Event Loop: │
│ │
│ PTY readable --> process bytes --> send Wakeup event │
│ │ │
│ v │
│ No wait for render │
│ │
└───────────────────────────────────────────────────────────────────────┘
Ghostty
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ GHOSTTY: Fixed Capacity Queue │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Architecture: │
│ │
│ Fixed-capacity message queue │
│ Draw interval targets ~120 FPS │
│ │
│ Behavior: │
│ │
│ When queue full: Producer blocks │
│ When queue empty: Consumer waits │
│ │
│ Limitation: │
│ │
│ Buffer-based, NOT render-synchronized │
│ Queue depth != render completion │
│ │
└───────────────────────────────────────────────────────────────────────┘
xterm.js
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ XTERM.JS: Parser Buffer Limits │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Write Buffer Configuration: │
│ │
│ Discard watermark: ~50 MB │
│ Write timeout: 12ms between writes │
│ │
│ The Gap: │
│ │
│ Callback fires AFTER parser processes data │
│ Callback does NOT wait for DOM render │
│ │
│ Timeline: │
│ │
│ write(data) --> parse --> callback() --> ... --> DOM render │
│ │ │ │
│ fires here happens later │
│ │
│ Result: No feedback loop exists │
│ │
└───────────────────────────────────────────────────────────────────────┘
Traditional vs MonoTerm
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ OPEN LOOP: Traditional Terminals │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ PTY Data │
│ │ │
│ v │
│ Parser │
│ │ (callback or event) │
│ v │
│ Renderer Queue │
│ │ │
│ v │
│ DOM/GPU Render │
│ │ │
│ v │
│ [NOTHING SENT BACK] <── No feedback to producer │
│ │
│ Producer continues at full speed regardless of consumer state │
│ │
└───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐
│ CLOSED LOOP: MonoTerm │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ PTY Data │
│ │ │
│ v │
│ VTE Parser │
│ │ │
│ v │
│ AtomicState.pull() [waiting_ack?] │
│ │ │
│ ├── YES --> return None (skip emit, continue parsing) │
│ │ │
│ └── NO --> emit GridUpdate │
│ │ │
│ v │
│ Frontend receives │
│ │ │
│ v │
│ inject() to xterm.js │
│ │ │
│ v │
│ [Success?] │
│ │ │
│ ├── YES --> grid_ack() --> waiting_ack = false │
│ │ │
│ └── NO --> No ACK (backend timeout retry) │
│ │
│ Consumer controls producer speed through ACK after render │
│ │
└───────────────────────────────────────────────────────────────────────┘
Summary
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ KEY INSIGHT │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ All traditional terminals use OPEN LOOP architecture: │
│ │
│ - Producer (PTY) sends data at its own pace │
│ - No feedback from renderer to producer │
│ - Various buffering strategies to manage overflow │
│ │
│ MonoTerm uses CLOSED LOOP architecture: │
│ │
│ - Consumer (renderer) controls the pace via ACK │
│ - Producer waits for confirmation before next emit │
│ - Frame synchronization at IPC boundary │
│ │
└───────────────────────────────────────────────────────────────────────┘
Related Research
- Prior Art - Design rationale and learnings