Skip to main content

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}