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.