Async Rust is not safe with io_uring
Safety in Rust: memory vs I/O vs leaks
- Commenters stress the issue is not memory unsafety or UB.
- Rust’s notion of “safety” covers memory safety (no corruption, UB, data races), not resource leaks.
- I/O safety RFC is about preventing unauthorized handle access, not guaranteeing clean shutdowns.
- Several people say the article conflates “I/O safety” with “not leaking FDs,” which is a regular bug, not “unsafety” in Rust’s usual sense.
Async Rust model and cancellation
- Rust Futures assume: progress only when polled, and cancellation by dropping the future.
- This works well with epoll-style readiness, where spurious wakeups are cheap.
- With io_uring and Windows-style completion APIs, operations may complete in the kernel after a future is “cancelled,” creating tricky races.
- Lack of “async drop” makes non-blocking cleanup on cancellation difficult.
io_uring integration challenges
- The core bug discussed is FD leaks (and possibly dropped connections) when accept futures are cancelled while an io_uring accept completes.
- Some argue this is a known, fundamental race in any cancellation model: cancellation can always lose to completion.
- Others say a correct executor can still manage this by tracking completions and either cancelling or cleaning up resources when tasks lose interest.
Is this a Rust problem or a library problem?
- Many commenters frame it as a flawed or incomplete io_uring runtime API (e.g., monoio), not a flaw in “Async Rust” itself.
- Examples are given of alternative designs that:
- store completed-but-unconsumed accepts and yield them later, or
- register cancellation callbacks that run when completions arrive.
- There is disagreement whether Async Rust and io_uring are fundamentally mismatched or just require careful reactor design.
Broader views on async, APIs, and abstractions
- Some see this as another example of async Rust being easy to misuse and argue many codebases should avoid async entirely.
- Others defend async/await and Rust’s model as powerful but unfinished and demanding careful engineering.
- A side discussion notes that each OS async mechanism shift (select → epoll → io_uring) tends to invalidate large parts of existing abstractions; generic “one-size-fits-all” async layers are hard to design without leaks in the abstraction.