Security - Remove the unsafe Vec::set_len over recycled sector buffers — UB reachable from safe code (GHSA-j8ww-f5fg-9pmh, low). Uses Vec::resize; pooling unchanged. CI / tooling - clippy on stable with the MSRV single-sourced from Cargo.toml; cargo-deny dependency-audit gate with first-party git sources allowed (#15); audience-based comment-guard (#17). Docs - README (keydb ownership, BD-J parser count), drive-access transport/reset notes, mux timeline module-doc links.
2.7 KiB
src/io/bounded.rs — bounded-syscall primitive
Why this exists
[crate::halt::Halt] is cooperative: callers poll is_cancelled(). It
cannot reach inside a syscall the kernel currently owns the thread for —
libc::sync_file_range, libc::fsync, File::write on NFS, and so on.
/api/stop from autorip therefore can't unstick a thread sitting in
such a syscall.
bounded_syscall is the escape hatch: it runs op on a fresh worker
thread, then recv_timeouts on a rendezvous channel for the result.
The wait is broken into ~250 ms slices so the calling thread can poll
the supplied Halt in between. If the deadline elapses or the halt
fires, the worker is intentionally leaked — the syscall will unwind
whenever the kernel decides, or at process exit, but the caller is
free to fall back to a degraded code path (skip the sync, log loudly,
etc.).
Trade-offs
- Thread per call. Cheap (
std::thread::spawnis < 100 µs on Linux/macOS), but not free. Use on coarse-grained finalisation syscalls (sync_all,sync_file_range(WAIT_AFTER)), not on hot inner-loop writes. - Leak on timeout. A wedged syscall keeps a kernel slot and a
user-space thread around for the rest of the process's life. Bounded
by the number of independent rip/mux sessions, which is one per
disc. The alternative — trapping the caller forever — defeats the
entire purpose of
/api/stop. - Halt granularity ~250 ms. Halt observation is not instant; it's
the worst-case latency of the
recv_timeoutslice. Good enough for human-driven stop requests; not suitable for hard real-time deadlines.
Single source of truth
Do NOT inline this pattern. Every blocking-syscall wrapper in the rip
- mux pipeline calls this helper, so changes (e.g. swapping the channel impl, adjusting the poll slice, adding metrics) land in one place.
Platform
Pure std::thread + std::sync::mpsc. No cfg(target_os) needed
here — the helper itself is platform-agnostic. Callers that wrap
Linux-only syscalls (sync_file_range) still need their own
#[cfg(target_os = "linux")] gates; this helper does not.
bounded_syscall contract details
haltis polled atPOLL_INTERVALgranularity. PassNonefor callers that don't (yet) have a halt token plumbed through — behaviour degrades to deadline-only, matching the 0.20.5wait_after_with_timeoutshape this helper generalises.opreturnsR: Send + 'static. The closure must own everything it touches because it may outlive this call (timeout / halt cases).- On
Halted/Timeoutthe worker thread is intentionally leaked: the syscall will unwind whenever the kernel decides, or when the process exits. The calling thread is never trapped inside a kernel call.