conmonrs/async_fd.rs
1//! Waiting for descriptors which tokio has no type of its own for, like
2//! pidfds, terminals, epoll and inotify instances.
3
4use std::{
5 io,
6 ops::Deref,
7 os::fd::{AsFd, AsRawFd, RawFd},
8};
9use tokio::io::{Interest, unix::AsyncFd};
10
11/// A value holding a descriptor, which tokio waits for while it is registered,
12/// see [`register`].
13#[derive(Debug)]
14pub(crate) struct Fd<T> {
15 inner: T,
16 raw: RawFd,
17}
18
19impl<T> Deref for Fd<T> {
20 type Target = T;
21
22 fn deref(&self) -> &T {
23 &self.inner
24 }
25}
26
27impl<T> AsRawFd for Fd<T> {
28 fn as_raw_fd(&self) -> RawFd {
29 self.raw
30 }
31}
32
33/// Register the descriptor of a value with the reactor of the runtime, the
34/// returned [`AsyncFd`] owns the value.
35///
36/// The [`Fd`] has to stay in its [`AsyncFd`]: the mutable accessors of it,
37/// like [`AsyncFd::get_mut`], must not be used to swap it with the one of
38/// another registration, see the safety comment below.
39pub(crate) fn register<T: AsFd>(inner: T, interest: Interest) -> io::Result<AsyncFd<Fd<T>>> {
40 let raw = inner.as_fd().as_raw_fd();
41 #[expect(unsafe_code, reason = "registering a descriptor with tokio")]
42 // SAFETY: the descriptor is the one taken here, which `Fd` returns from
43 // then on. It stays open and refers to the same file for as long as the
44 // value lives: I/O safety does not let shared access close or replace
45 // the descriptor of an `AsFd`, and `Fd` hands out nothing else of the
46 // value. The `AsyncFd` owns the `Fd`, but nothing enforces that it keeps
47 // it: its mutable accessors would let safe code swap two of them, each
48 // registration would have the descriptor of the other one then. No
49 // caller uses them, and none may.
50 let fd = unsafe { AsyncFd::register_with_interest(Fd { inner, raw }, interest) }?;
51 Ok(fd)
52}