Skip to main content

netwatcher/
async_adapter.rs

1use std::{future::Future, pin::Pin};
2
3#[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
4use std::marker::PhantomData;
5
6#[cfg(all(
7    any(feature = "async-io", feature = "tokio"),
8    any(target_os = "linux", target_vendor = "apple")
9))]
10use std::io;
11#[cfg(all(
12    any(feature = "async-io", feature = "tokio"),
13    any(target_os = "linux", target_vendor = "apple")
14))]
15use std::os::fd::AsFd;
16#[cfg(any(target_os = "linux", target_vendor = "apple"))]
17use std::os::fd::{BorrowedFd, OwnedFd};
18
19/// Runtime-owned readiness source passed to an [`AsyncFdAdapter`].
20///
21/// On Linux and Apple platforms this wraps the nonblocking watch file descriptor.
22/// On other platforms the value is never used.
23pub struct AsyncFd {
24    #[cfg(any(target_os = "linux", target_vendor = "apple"))]
25    inner: OwnedFd,
26    #[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
27    _private: (),
28}
29
30impl AsyncFd {
31    #[cfg(any(target_os = "linux", target_vendor = "apple"))]
32    pub(crate) fn from_owned_fd(inner: OwnedFd) -> Self {
33        Self { inner }
34    }
35
36    #[cfg(any(target_os = "linux", target_vendor = "apple"))]
37    pub fn into_owned_fd(self) -> OwnedFd {
38        self.inner
39    }
40}
41
42/// Borrowed readiness source returned by an [`AsyncFdReadyGuard`].
43///
44/// On Linux and Apple platforms this wraps the watch file descriptor.
45/// On other platforms the value is never used.
46pub struct AsyncFdRef<'a> {
47    #[cfg(any(target_os = "linux", target_vendor = "apple"))]
48    inner: BorrowedFd<'a>,
49    #[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
50    _marker: PhantomData<&'a ()>,
51}
52
53impl<'a> AsyncFdRef<'a> {
54    #[cfg(any(target_os = "linux", target_vendor = "apple"))]
55    pub fn from_borrowed_fd(inner: BorrowedFd<'a>) -> Self {
56        Self { inner }
57    }
58
59    #[cfg(any(target_os = "linux", target_vendor = "apple"))]
60    pub fn as_fd(&self) -> BorrowedFd<'_> {
61        self.inner
62    }
63}
64
65/// A runtime adapter that can register an existing nonblocking file descriptor for async waiting.
66///
67/// On Windows and Android, the adapter type is still required for API consistency, but the
68/// platform watcher uses callback-driven notifications and does not invoke the adapter.
69pub trait AsyncFdAdapter {
70    fn register(fd: AsyncFd) -> std::io::Result<Box<dyn AsyncFdRegistration>>;
71}
72
73pub type AsyncFdReadableFuture<'a> =
74    Pin<Box<dyn Future<Output = std::io::Result<Box<dyn AsyncFdReadyGuard + 'a>>> + 'a>>;
75
76/// Registered readiness source for a watch file descriptor.
77pub trait AsyncFdRegistration: Send + Sync {
78    fn readable(&self) -> AsyncFdReadableFuture<'_>;
79}
80
81/// Guard returned once the runtime reports the watch file descriptor as readable.
82pub trait AsyncFdReadyGuard {
83    fn fd(&self) -> AsyncFdRef<'_>;
84    fn clear_ready(&mut self);
85}
86
87#[cfg(feature = "async-io")]
88pub struct AsyncIo;
89
90#[cfg(feature = "tokio")]
91pub struct Tokio;
92
93#[cfg(all(feature = "tokio", any(target_os = "linux", target_vendor = "apple")))]
94impl AsyncFdAdapter for Tokio {
95    fn register(fd: AsyncFd) -> io::Result<Box<dyn AsyncFdRegistration>> {
96        Ok(Box::new(tokio::io::unix::AsyncFd::new(fd.into_owned_fd())?))
97    }
98}
99
100#[cfg(all(feature = "tokio", any(target_os = "linux", target_vendor = "apple")))]
101impl AsyncFdRegistration for tokio::io::unix::AsyncFd<OwnedFd> {
102    fn readable(&self) -> AsyncFdReadableFuture<'_> {
103        Box::pin(async move {
104            let guard = self.readable().await?;
105            Ok(Box::new(guard) as Box<dyn AsyncFdReadyGuard>)
106        })
107    }
108}
109
110#[cfg(all(feature = "tokio", any(target_os = "linux", target_vendor = "apple")))]
111impl AsyncFdReadyGuard for tokio::io::unix::AsyncFdReadyGuard<'_, OwnedFd> {
112    fn fd(&self) -> AsyncFdRef<'_> {
113        AsyncFdRef::from_borrowed_fd(self.get_inner().as_fd())
114    }
115
116    fn clear_ready(&mut self) {
117        tokio::io::unix::AsyncFdReadyGuard::clear_ready(self);
118    }
119}
120
121#[cfg(all(feature = "tokio", any(windows, target_os = "android")))]
122impl AsyncFdAdapter for Tokio {
123    fn register(_fd: AsyncFd) -> std::io::Result<Box<dyn AsyncFdRegistration>> {
124        unreachable!("Tokio AsyncFd registration is not used on this platform")
125    }
126}
127
128#[cfg(all(
129    feature = "async-io",
130    any(target_os = "linux", target_vendor = "apple")
131))]
132impl AsyncFdAdapter for AsyncIo {
133    fn register(fd: AsyncFd) -> io::Result<Box<dyn AsyncFdRegistration>> {
134        Ok(Box::new(AsyncIoRegistration(async_io::Async::new(
135            fd.into_owned_fd(),
136        )?)))
137    }
138}
139
140#[cfg(all(
141    feature = "async-io",
142    any(target_os = "linux", target_vendor = "apple")
143))]
144struct AsyncIoRegistration(async_io::Async<OwnedFd>);
145
146#[cfg(all(
147    feature = "async-io",
148    any(target_os = "linux", target_vendor = "apple")
149))]
150struct AsyncIoReadyGuard<'a>(&'a async_io::Async<OwnedFd>);
151
152#[cfg(all(
153    feature = "async-io",
154    any(target_os = "linux", target_vendor = "apple")
155))]
156impl AsyncFdRegistration for AsyncIoRegistration {
157    fn readable(&self) -> AsyncFdReadableFuture<'_> {
158        Box::pin(async move {
159            self.0.readable().await?;
160            Ok(Box::new(AsyncIoReadyGuard(&self.0)) as Box<dyn AsyncFdReadyGuard>)
161        })
162    }
163}
164
165#[cfg(all(
166    feature = "async-io",
167    any(target_os = "linux", target_vendor = "apple")
168))]
169impl AsyncFdReadyGuard for AsyncIoReadyGuard<'_> {
170    fn fd(&self) -> AsyncFdRef<'_> {
171        AsyncFdRef::from_borrowed_fd(self.0.get_ref().as_fd())
172    }
173
174    fn clear_ready(&mut self) {}
175}
176
177#[cfg(all(feature = "async-io", any(windows, target_os = "android")))]
178impl AsyncFdAdapter for AsyncIo {
179    fn register(_fd: AsyncFd) -> std::io::Result<Box<dyn AsyncFdRegistration>> {
180        unreachable!("async-io AsyncFd registration is not used on this platform")
181    }
182}