Skip to main content

Actor + ACK Synergy

Actor orders the processing, ACK controls the emission. Together they form a complete flow control system.

Two Patterns, Two Origins, One Goal

┌─────────────────────────────────────────────────────────────────┐
│  TWO PATTERNS WORKING TOGETHER                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  +----------------------------+------------------------------+  │
│  |      ACTOR PATTERN         |       ACK PATTERN            |  │
│  +----------------------------+------------------------------+  │
│  |                            |                              |  │
│  | Origin: Erlang (1973/1986) | Origin: TCP/IP (1974)        |  │
│  |                            |                              |  │
│  | Purpose: Concurrency       | Purpose: Flow control        |  │
│  |          without locks     |          between endpoints   |  │
│  |                            |                              |  │
│  | Mechanism: Message passing | Mechanism: Acknowledgment    |  │
│  |            Single owner    |            "Ready for more"  |  │
│  |                            |                              |  │
│  | Guarantees: Ordered        | Guarantees: No overflow      |  │
│  |             No races       |             Receiver-paced   |  │
│  |                            |                              |  │
│  +----------------------------+------------------------------+  │
│                                                                 │
│  In MonoTerm, they work TOGETHER:                               │
│                                                                 │
│  Actor: "I process PTY data in order, no locks"                 │
│                    |                                            │
│                    v                                            │
│  ACK: "I only emit to frontend when it is ready"                │
│                                                                 │
│  Combined: Ordered processing + Paced emission                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Complete Data Flow

┌─────────────────────────────────────────────────────────────────┐
│  COMPLETE DATA FLOW: PTY TO SCREEN                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  STAGE 1: PTY DAEMON (Rust Sidecar)                             │
│  -------------------------------------------------------        │
│                                                                 │
│  Shell/Command ---> PTY Master ---> Unix Socket ---> (Tauri)    │
│                                                                 │
│  * Pure byte stream                                             │
│  * Unbounded channel (no data loss)                             │
│  * No knowledge of ACK                                          │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  STAGE 2: TAURI BACKEND (Rust) - ACTOR DOMAIN                   │
│  -------------------------------------------------------        │
│                                                                 │
│  +------------+     +-----------------------------------------+ │
│  | Socket     |     |           SessionActor                  | │
│  | Reader     |---->|                                         | │
│  +------------+     |  rx.recv() --> match cmd {              | │
│                     |                   HandlePtyData => {    | │
│                     |                     grid_worker.send()  | │
│                     |                   }                     | │
│                     |               }                         | │
│                     |                                         | │
│                     |  GUARANTEES:                            | │
│                     |  * Sequential processing                | │
│                     |  * No lock contention                   | │
│                     |  * Order preserved                      | │
│                     +-----------------+-----------------------+ │
│                                       |                         │
│                                       v                         │
│                     +-----------------------------------------+ │
│                     |           GridWorker                    | │
│                     |                                         | │
│                     |  Alacritty VTE Parser                   | │
│                     |           |                             | │
│                     |           v                             | │
│                     |  State Absorber (overwrite, not queue)  | │
│                     |           |                             | │
│                     |           v                             | │
│                     |  if !waiting_ack {       <-- ACK CHECK  | │
│                     |      emit("pty-grid")                   | │
│                     |      waiting_ack = true                 | │
│                     |  }                                      | │
│                     +-----------------------------------------+ │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  STAGE 3: FRONTEND (TypeScript) - ACK SOURCE                    │
│  -------------------------------------------------------        │
│                                                                 │
│  listen("pty-grid") --> xterm.js render --> invoke("grid_ack")  │
│                                                    |            │
│                                                    v            │
│                                           "I am ready for more" │
│                                                                 │
│  GUARANTEES:                                                    │
│  * Renderer never overwhelmed                                   │
│  * Screen always stable                                         │
│  * No frame drops                                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

ACK Handshake Mechanism

┌─────────────────────────────────────────────────────────────────┐
│  ACK STATE MACHINE                                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                   +----------------------------------+          │
│                   |                                  |          │
│  +----------------v----------------+    emit()       |          │
│  |                                 | ----------------+          │
│  |     waiting_ack = false         |                            │
│  |      (READY TO EMIT)            |                            │
│  |                                 |                            │
│  +----------------+----------------+                            │
│                   |                                             │
│                   | new PTY data arrives                        │
│                   | emit("pty-grid")                            │
│                   | waiting_ack = true                          │
│                   v                                             │
│  +---------------------------------+                            │
│  |                                 |                            │
│  |     waiting_ack = true          |  <-- More PTY data arrives │
│  |     (WAITING FOR ACK)           |      State absorbs, no emit│
│  |                                 |                            │
│  +----------------+----------------+                            │
│                   |                                             │
│                   | Frontend: invoke("grid_ack")                │
│                   | waiting_ack = false                         │
│                   |                                             │
│                   +---------------------------------------------│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Timeline Example

┌─────────────────────────────────────────────────────────────────┐
│  ACK TIMELINE                                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  T=0ms   PTY data, waiting_ack=false -> emit, waiting_ack=true  │
│  T=5ms   PTY data, waiting_ack=true  -> State absorbs (NO emit) │
│  T=10ms  PTY data, waiting_ack=true  -> State absorbs (NO emit) │
│  T=15ms  PTY data, waiting_ack=true  -> State absorbs (NO emit) │
│  T=16ms  Frontend renders, grid_ack() -> waiting_ack=false      │
│  T=16ms  New state ready -> emit, waiting_ack=true              │
│  ...                                                            │
│                                                                 │
│  Result: 4 PTY updates, 2 emits, 0 frame drops, smooth 60fps    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Why Both Patterns Are Needed

┌─────────────────────────────────────────────────────────────────┐
│  ACTOR WITHOUT ACK                                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  PTY: 1000 messages/sec                                         │
│       |                                                         │
│       v                                                         │
│  Actor: process all 1000 sequentially (order OK)                │
│       |                                                         │
│       v                                                         │
│  Emit: 1000 events/sec to Frontend                              │
│       |                                                         │
│       v                                                         │
│  Frontend: Can only render 60/sec                               │
│       |                                                         │
│       v                                                         │
│  Result: Event queue grows, memory grows, crash                 │
│                                                                 │
│  Actor guarantees ORDER, not PACE.                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  ACK WITHOUT ACTOR                                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  PTY: 1000 messages/sec                                         │
│       |                                                         │
│       v                                                         │
│  Mutex-protected state: lock contention, possible reordering    │
│       |                                                         │
│       v                                                         │
│  ACK: Controls emission (pace OK)                               │
│       |                                                         │
│       v                                                         │
│  Result: State updates may race, inconsistent display           │
│                                                                 │
│  ACK guarantees PACE, not ORDER.                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  ACTOR + ACK TOGETHER                                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  PTY: 1000 messages/sec                                         │
│       |                                                         │
│       v                                                         │
│  Actor: process all 1000 sequentially  <-- ORDER                │
│       |                                                         │
│       v                                                         │
│  State Absorber: coalesce into single state                     │
│       |                                                         │
│       v                                                         │
│  ACK: emit only when ready  <-- PACE                            │
│       |                                                         │
│       v                                                         │
│  Frontend: receives 60 states/sec, all consistent               │
│       |                                                         │
│       v                                                         │
│  Result: Smooth, ordered, memory-stable                         │
│                                                                 │
│  ACTOR + ACK = ORDER + PACE                                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

The Synergy Diagram

┌─────────────────────────────────────────────────────────────────┐
│  ACTOR + ACK: UNIFIED VIEW                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   PTY Daemon                                                    │
│   +-----------+                                                 │
│   | Unbounded |                                                 │
│   | Channel   | -----------------------------------------+      │
│   +-----------+                                          |      │
│        |                                                 |      │
│        | Unix Socket                                     |      │
│        v                                                 |      │
│   +--------------------------------------------------------+    │
│   |                    TAURI BACKEND                       |    │
│   |                                                        |    │
│   |  +-----------+                                         |    │
│   |  |  Reader   |                                         |    │
│   |  +-----------+                                         |    │
│   |       | tx.send(HandlePtyData)                         |    │
│   |       v                                                |    │
│   |  +----------------------------------------------+      |    │
│   |  |              SessionActor                    |      |    │
│   |  |                                              |      |    │
│   |  | [ACTOR GUARANTEE: Sequential, No Locks]     |      |     │
│   |  |                   |                          |      |    │
│   |  |                   v                          |      |    │
│   |  |           GridWorker.send(data)              |      |    │
│   |  +----------------------+-----------------------+      |    │
│   |                         |                              |    │
│   |                         v                              |    │
│   |  +----------------------------------------------+      |    │
│   |  |              GridWorker                      |      |    │
│   |  |                                              |      |    │
│   |  |  Alacritty VTE Parser                        |      |    │
│   |  |         |                                    |      |    │
│   |  |         v                                    |      |    │
│   |  |  State Absorber (overwrites, always current) |      |    │
│   |  |         |                                    |      |    │
│   |  |         v                                    |      |    │
│   |  | [ACK GUARANTEE: Emit only when ready]        |      |    │
│   |  |                                              |      |    │
│   |  +----------------------+-----------------------+      |    │
│   |                         |                              |    │
│   +-------------------------+------------------------------+    │
│                             |                                   │
│                             | emit("pty-grid")                  │
│                             v                                   │
│   +--------------------------------------------------------+    │
│   |                       FRONTEND                          |   │
│   |                                                         |   │
│   |  listen("pty-grid") --> xterm.js --> invoke("grid_ack") |---+
    │   |                                                         |
     │   +--------------------------------------------------------+
│                                                                 │
│   RESULT: 1000 updates/sec -> 60 screen updates/sec             │
│           All ordered, no memory growth                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Data Reduction Through Synergy

┌─────────────────────────────────────────────────────────────────┐
│  DATA REDUCTION                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  INPUT: 1000 PTY updates/second                                 │
│                                                                 │
│  +------------------+-------------+-------------+--------------+│
│  | Stage            | Updates     | Reduction   | Cumulative   |│
│  +------------------+-------------+-------------+--------------+│
│  | PTY Output       | 1000/sec    | -           | 1000/sec     |│
│  |     |            |             |             |              |│
│  |     v            |             |             |              |│
│  | Actor (order)    | 1000/sec    | 0%          | 1000/sec     |│
│  |     |            |             |             | (ordered)    |│
│  |     v            |             |             |              |│
│  | State Absorber   | 1000/sec    | 0%*         | 1000/sec     |│
│  |     |            | *processes  |             | (coalesced)  |│
│  |     v            | all, stores |             |              |│
│  | ACK Gate         | 60/sec      | 94%         | 60/sec       |│
│  |     |            |             |             |              |│
│  |     v            |             |             |              |│
│  | Frontend Render  | 60/sec      | 0%          | 60/sec       |│
│  +------------------+-------------+-------------+--------------+│
│                                                                 │
│  TOTAL REDUCTION: 1000 -> 60 = 94% fewer events to frontend     │
│                                                                 │
│  All 1000 updates are PROCESSED (no data loss)                  │
│  Only EMISSION is reduced                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Summary

┌─────────────────────────────────────────────────────────────────┐
│  ACTOR + ACK: KEY TAKEAWAYS                                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. Actor guarantees ORDER (sequential, no races)               │
│  2. ACK guarantees PACE (emit only when frontend ready)         │
│  3. State Absorber bridges them (coalescing)                    │
│  4. Together: ORDER + PACE + MEMORY STABILITY                   │
│                                                                 │
│  -----------------------------------------------------------    │
│                                                                 │
│  Neither pattern alone solves high-output terminal rendering.   │
│  Together, they form a complete flow control system.            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘