Skip to main content

Module offload

Module offload 

Source
Expand description

Run CPU-bound handler work off the async runtime worker.

tower-lsp drives every connection’s requests from the same small tokio worker pool. A handler that walks an AST, scans the workspace index, or shells out to an external process inline blocks that worker — every other in-flight request (on this connection or, with a multi-threaded runtime, any other task sharing the pool) stalls until it finishes. See the git history for the long tail of fix(lsp): run X off the request loop commits this fixed one handler at a time before this module existed.

run and run_gated are the two sanctioned ways to leave the async runtime worker: tokio::task::spawn_blocking plus uniform panic handling. A spawn_blocking panic is delivered as Err(JoinError) rather than propagating like a normal panic; before this module several call sites discarded that Err via .unwrap_or_default() with no logging at all, so a panicking closure failed silently. Both helpers log unconditionally and return None, so callers can never reintroduce that gap by accident — .unwrap_or_default(), .unwrap_or(fallback), and let Some(x) = ... else { return .. } all stay available on the Option they return.

run_gated additionally passes through a named DebugGate section first, so a responsiveness regression test can pin that specific closure in flight and assert the connection still answers other requests meanwhile (see debug_gate module docs). Reach for run when a closure has no such test.

Functions§

run
Runs f on the blocking thread pool. Returns None (logging via label) if f panics.
run_gated
Same as run, but f first passes through section on gate — a no-op outside test builds (see debug_gate).