coalesce-worker
A coalescing worker thread with generation-counter stale-result rejection — the discipline needed to run tree-sitter (or any expensive computation) off the main thread without applying out-of-date results.
Zero dependencies. Originally extracted from a tree-sitter syntax highlighter, but the pattern fits any "process this data, discard if newer arrived" background task.
Also known as / if you're searching for: request coalescing, debouncing a background worker, stale-result rejection, latest-wins task queue, async tree-sitter highlighting in Rust, canceling superseded parse jobs.
The problem
An editor offloads syntax highlighting to a background thread. The user types fast; the main thread fires off highlight requests, one per keystroke. The worker can only process one at a time. By the time request N-5 finishes, the source is at state N and the spans computed from the stale N-5 source point at byte offsets that no longer exist. If the main thread applies them anyway the UI corrupts.
This is the "stale async cache after mutation" failure mode
documented in .claude/rules/common/classic-errors.md.
The fix
Two disciplines enforced by this crate:
- Request coalescing — the worker drains its queue before each job; older requests that never started are silently dropped.
- Generation counting — every submitted request gets a monotonic
generation number.
poll()drains all pending results and returns only the newest; older results are discarded.
API
use ;
;
let mut c = new;
// main loop
let gen = c.submit;
if let Some = c.poll
Using it with tree-sitter
use Arc;
use ;
use ;
let mut c = new;
// ... per keystroke: c.submit(HighlightRequest { source, config });
// ... per frame: c.poll();
Context-switch discipline
When switching buffers (tab change, file close), any in-flight response
for the previous buffer will still arrive. If applied, it corrupts
the new buffer. Call flush_pending() on context switch:
# use ;
# ;
# let mut c = new;
c.flush_pending;
// then submit requests for the new context
Demo
Submits 20 requests rapid-fire to a worker that takes 150ms per job. Prints each submit + each receive, showing generation gaps (proof that intermediate requests were coalesced away and never ran).
Install
[]
= "0.1"
Origin
Extracted from goliajp/tora —
crates/tora-syntax/src/async_highlighter.rs, where it drove
syntax highlighting for 19 tree-sitter languages in a GUI editor.
License
MIT — see LICENSE.