Response Timeout
Request-response patterns need timeouts. Without them, a single unresponsive Actor can freeze the entire application.The Pattern: Request-Response
Copy
┌─────────────────────────────────────────────────────────────────┐
│ ONESHOT CHANNEL: REQUEST-RESPONSE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ IPC Handler (Tauri command) SessionActor │
│ │ │ │
│ │ 1. Create oneshot channel │ │
│ │ (for response) │ │
│ │ │ │
│ │ ════════ Command ════════════▶ │ │
│ │ (includes response channel) │ │
│ │ │ │
│ │ 2. Wait for response │ 3. Process │
│ │ ⏳ WAITING... │ 4. Send OK │
│ │ │ │
│ │ ◀════════ Response ═══════════ │ │
│ │ │ │
│ │
└─────────────────────────────────────────────────────────────────┘
The Problem: Infinite Wait
Copy
┌─────────────────────────────────────────────────────────────────┐
│ BUG: NO TIMEOUT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ BEFORE FIX: │
│ ━━━━━━━━━━━━ │
│ │
│ 1. Send command to Actor │
│ 2. Wait for response │
│ ← NO TIMEOUT! │
│ ← If Actor doesn't respond → wait FOREVER │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ FAILURE SCENARIO 1: Actor Crash │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ IPC Handler SessionActor │
│ │ │ │
│ │ ──── CreateSession ────────▶ │ │
│ │ 💥 panic! │
│ │ │ │
│ │ ⏳ waiting... ✖ (dead) │
│ │ ⏳ waiting... │
│ │ ⏳ FOREVER │
│ │ │
│ Result: IPC blocked, user sees frozen UI │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ FAILURE SCENARIO 2: Actor Busy │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ IPC Handler SessionActor │
│ │ │ │
│ │ ──── CreateSession ────────▶ │ │
│ │ 🔄 (stuck) │
│ │ ⏳ waiting... 🔄 (never processes) │
│ │ ⏳ waiting... 🔄 │
│ │ ⏳ FOREVER 🔄 │
│ │ │
│ Result: IPC blocked indefinitely │
│ │
└─────────────────────────────────────────────────────────────────┘
The Fix: Timeout Wrapper
Copy
┌─────────────────────────────────────────────────────────────────┐
│ TIMEOUT ERROR CHAIN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ timeout(10 seconds, wait_for_response) │
│ │ │
│ ├── SUCCESS ────────────────▶ Response received │
│ │ │
│ ├── TIMEOUT ────────────────▶ Error: "timeout" │
│ │ (after 10 seconds) │
│ │ │
│ └── CHANNEL CLOSED ─────────▶ Error: "Actor dropped" │
│ (Actor died) │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ AFTER FIX: │
│ ━━━━━━━━━━━ │
│ │
│ 1. Send command to Actor │
│ 2. Wait for response WITH 10 SECOND TIMEOUT │
│ 3. If timeout → return error to frontend │
│ 4. User sees error message, app stays responsive │
│ │
└─────────────────────────────────────────────────────────────────┘
Functions Protected (8 Total)
Copy
┌─────────────────────────────────────────────────────────────────┐
│ FUNCTIONS WITH TIMEOUT APPLIED │
├─────────────────────────────────────────────────────────────────┤
│ │
│ +------------------------+------------------------------------+│
│ | Function | Description |│
│ +------------------------+------------------------------------+│
│ | create_session | Create new terminal session |│
│ | broadcast_to_visible | Send data to visible sessions |│
│ | resize_session | Resize terminal window |│
│ | close_session | Close terminal session |│
│ | kill_session | Force kill session |│
│ | terminal_search | Search in terminal buffer |│
│ | pty_pause_native | Pause PTY output |│
│ | pty_resume_native | Resume PTY output |│
│ +------------------------+------------------------------------+│
│ │
│ All 8 functions follow the same pattern: │
│ │
│ 1. Create oneshot channel │
│ 2. Send command to Actor │
│ 3. Wait for response WITH 10 SECOND TIMEOUT │
│ │
└─────────────────────────────────────────────────────────────────┘
Why 10 Seconds?
Copy
┌─────────────────────────────────────────────────────────────────┐
│ TIMEOUT VALUE ANALYSIS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ NORMAL RESPONSE TIME: │
│ ━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Typical Actor response: < 1ms │
│ │
│ ┌──────┬──────┬──────┬──────┬──────┬──────┐ │
│ │ <1ms │ │ │ │ │ 10s │ │
│ └──────┴──────┴──────┴──────┴──────┴──────┘ │
│ normal timeout │
│ │◀──────────────────────────────────▶│ │
│ 10,000x safety margin │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ WHY 10 SECONDS? │
│ ━━━━━━━━━━━━━━━ │
│ │
│ +---------------------+---------------------------------------+│
│ | Value | Trade-off |│
│ +---------------------+---------------------------------------+│
│ | 1 second | May timeout during heavy load |│
│ | | (false positive) |│
│ +---------------------+---------------------------------------+│
│ | 5 seconds | Reasonable but tight |│
│ +---------------------+---------------------------------------+│
│ | 10 seconds ✓ | Safe margin, still responsive |│
│ +---------------------+---------------------------------------+│
│ | 30 seconds | User waits too long before error |│
│ +---------------------+---------------------------------------+│
│ | 60 seconds | Unacceptable UX |│
│ +---------------------+---------------------------------------+│
│ │
│ 10 seconds is the "Goldilocks" value: │
│ Not too short (false positives), not too long (bad UX) │
│ │
└─────────────────────────────────────────────────────────────────┘
Defense in Depth
Copy
┌─────────────────────────────────────────────────────────────────┐
│ TIMEOUT + PASSTHROUGH = COMPLETE SAFETY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ TWO COMPLEMENTARY PROTECTIONS: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. PASSTHROUGH DESIGN │ │
│ │ ──────────────────── │ │
│ │ • Actor doesn't die (OOM impossible, no panic) │ │
│ │ • Probability of needing timeout: ~0.001% │ │
│ │ │ │
│ │ 2. TIMEOUT │ │
│ │ ──────── │ │
│ │ • If Actor somehow fails, IPC doesn't hang │ │
│ │ • Graceful error handling │ │
│ │ • Defense in depth │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ DEFENSE IN DEPTH: │
│ │
│ Layer 1: Passthrough prevents Actor death │
│ → Timeout rarely triggers │
│ │
│ Layer 2: Timeout catches edge cases │
│ → Even if Actor fails, app doesn't freeze │
│ │
│ Result: System is robust against all failure modes │
│ │
└─────────────────────────────────────────────────────────────────┘
Summary
Copy
┌─────────────────────────────────────────────────────────────────┐
│ KEY TAKEAWAYS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. THE BUG │
│ Oneshot channels without timeout can block forever │
│ → Application appears frozen │
│ │
│ 2. THE FIX │
│ Wrap all response waits with timeout() │
│ → 10 second timeout for all 8 IPC handlers │
│ │
│ 3. WHY 10 SECONDS │
│ Normal response: < 1ms │
│ 10s = 10,000x safety margin │
│ → Catches real failures, avoids false positives │
│ │
│ 4. DEFENSE IN DEPTH │
│ Passthrough design: Actor rarely fails │
│ Timeout: Catches the rare failure gracefully │
│ → Combined = robust system │
│ │
└─────────────────────────────────────────────────────────────────┘