---
paths:
  - "src/**/*.rs"
  - "crates/**/*.rs"
---

# Async Rust with Tokio

- Never block the runtime: no `std::thread::sleep`, blocking file/network I/O, or heavy CPU loops inside async fns. Use `tokio::time::sleep`, `tokio::fs`, async clients, or `tokio::task::spawn_blocking` for CPU/sync work.
- Don't hold a `std::sync::Mutex` guard across `.await`. Use `tokio::sync::Mutex` only when the lock must span awaits; otherwise restructure to drop the guard first.
- Prefer message passing (`mpsc`, `oneshot`, `watch`, `broadcast`) for shared state owned by one task.
- Spawned tasks: keep the `JoinHandle` or use a `JoinSet`; handle `JoinError` (panics). Use `tokio_util::sync::CancellationToken` for shutdown.
- `select!` branches must be cancellation-safe; don't put non-cancel-safe futures (partial reads into a local buffer) directly in a loop'd `select!`.
- Put timeouts on all network calls (`tokio::time::timeout`).
- `Send` errors: usually a non-Send value (`Rc`, `RefCell`, a std guard) lives across an `.await`; scope it in a block instead of switching to a local runtime.
- `#[tokio::main]` only in binaries; libraries don't pick the runtime.
- Tests: `#[tokio::test]`, and `tokio::time::pause()` for time-based logic.

Verify with `cargo clippy -- -D warnings` (watch for `await_holding_lock`) and `cargo test`.
