Skip to main content

Terminal Architecture Comparison

Comparison of flow control mechanisms in modern terminals.

Executive Summary

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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                           │
│                                                                        │
└───────────────────────────────────────────────────────────────────────┘