Skip to main content

run_stdio

Function run_stdio 

Source
pub async fn run_stdio<R, W>(
    engine: Arc<RpcEngine>,
    reader: R,
    writer: W,
) -> Result<(), Error>
where R: AsyncBufRead + Unpin + Send + 'static, W: AsyncWrite + Unpin + Send + 'static,
Expand description

Drive the JSONL-RPC protocol over reader/writer (the stdio rung — parent-process-trusted, no auth token; see the module doc). Each request line is dispatched on its OWN spawned task so a submit in-flight never blocks the reader from picking up a subsequent interrupt/status line — every outgoing line (a response OR an event notification) is funneled through one mpsc channel into a single writer task, so two concurrent handlers can never interleave a line’s bytes. Returns once reader hits EOF or a shutdown request lands.

Both termination paths raise RpcEngine::signal_shutdown (EOF does it directly here; the shutdown RPC does it inside RpcEngine::handle_shutdown), and event_task below SELECTS against RpcEngine::wait_for_shutdown rather than merely looping on events.recv(). This is deliberate: events.recv() alone only ends via RecvError::Closed, which fires only once EVERY clone of engine.events (the broadcast Sender) has dropped — and engine itself, which keeps that Sender alive, is owned by THIS function for its whole body. Waiting on events.recv() to close would therefore mean waiting on engine to drop, which can’t happen until event_task itself finishes — a circular wait that never resolves (the bug this fn exists to fix). Selecting on the shutdown signal instead lets event_task end WITHOUT needing engine’s refcount to reach zero, so there is no orphaned task and no leaked engine/writer_task blocking on it in turn.