39 lines
1.9 KiB
Markdown
39 lines
1.9 KiB
Markdown
---
|
||
type: decision
|
||
adr: 005
|
||
status: accepted
|
||
date: 2026-10-17
|
||
supersedes:
|
||
tags: [adr, async, tokio, threads]
|
||
---
|
||
|
||
# ADR-005: adopt async gradually — sync core, then threads, then tokio
|
||
|
||
## Context
|
||
Ranger's fatal flaw is a synchronous loop that stats every file inline ([[research/ranger-deep-dive]]).
|
||
yazi proves tokio solves it but is a lot of machinery to learn while also learning Rust.
|
||
lf (Go goroutines) shows threads alone already keep a UI responsive.
|
||
|
||
## Options considered
|
||
| Option | Pros | Cons |
|
||
|--------|------|------|
|
||
| Gradual: sync → std threads + channels → tokio | learnable steps; working product at every stage | one refactor of the preview pipeline in Sprint 8 |
|
||
| tokio from day 1 | final architecture sooner | async lifetimes + borrow checker + TUI at once = frustration barrier |
|
||
| threads-only forever | simpler mental model | ecosystem (notify bridges, timeouts, process spawning) is tokio-first |
|
||
|
||
## Decision
|
||
- **Sprints 1–5**: synchronous core. The TUI loop owns all state; directory loading and file
|
||
ops are fast enough synchronously for a first product.
|
||
- **Sprints 6–7**: previews move to `std::thread` + `std::sync::mpsc` — UI stays responsive,
|
||
ownership lessons are explicit.
|
||
- **Sprint 8**: adopt **tokio** for the full preview/media pipeline (yazi architecture:
|
||
runtime workers marshal results to the single-threaded UI actor via a channel). Introduce
|
||
cancellation via a generation-counter ("ticket") pattern before adopting tokio `abort`.
|
||
|
||
## Consequences
|
||
- Positive: each concurrency model is learned in isolation; no big-bang async rewrite of the UI.
|
||
- Negative: preview pipeline is written twice (thread → task) — accepted as a deliberate
|
||
learning repetition (Kaufman: quantity of practice beats premature perfection).
|
||
- Mitigations: keep the preview pipeline behind a small interface (`Previewer` trait, ADR-002)
|
||
so the swap is local, not global.
|