Skip to main content

compio_runtime/fd/poll_fd/
mod.rs

1cfg_select! {
2    windows => {
3        #[path = "windows.rs"]
4        mod sys;
5    }
6    unix => {
7        #[path = "unix.rs"]
8        mod sys;
9    }
10    _ => {}
11}
12
13#[cfg(windows)]
14use std::os::windows::io::{AsRawSocket, RawSocket};
15use std::{
16    future::poll_fn,
17    io,
18    ops::Deref,
19    pin::Pin,
20    task::{Context, Poll},
21};
22
23use compio_buf::IntoInner;
24use compio_driver::{AsFd, AsRawFd, BorrowedFd, RawFd, SharedFd, ToSharedFd};
25
26/// Providing functionalities to wait for readiness.
27#[derive(Debug)]
28pub struct PollFd<T: AsFd>(sys::PollFd<T>);
29
30impl<T: AsFd> PollFd<T> {
31    /// Create [`PollFd`] without attaching the source.
32    ///
33    /// Ready-based sources does not need to be attached.
34    pub fn new(source: T) -> io::Result<Self> {
35        Self::from_shared_fd(SharedFd::new(source))
36    }
37
38    /// Create [`PollFd`] from a shared file descriptor.
39    pub fn from_shared_fd(inner: SharedFd<T>) -> io::Result<Self> {
40        Ok(Self(sys::PollFd::new(inner)?))
41    }
42}
43
44impl<T: AsFd + 'static> PollFd<T> {
45    /// Wait for accept readiness, before calling `accept`, or after `accept`
46    /// returns `WouldBlock`.
47    pub async fn accept_ready(&self) -> io::Result<()> {
48        poll_fn(|cx| self.poll_accept_ready(cx)).await
49    }
50
51    /// Wait for connect readiness.
52    pub async fn connect_ready(&self) -> io::Result<()> {
53        poll_fn(|cx| self.poll_connect_ready(cx)).await
54    }
55
56    /// Wait for read readiness.
57    pub async fn read_ready(&self) -> io::Result<()> {
58        poll_fn(|cx| self.poll_read_ready(cx)).await
59    }
60
61    /// Wait for write readiness.
62    pub async fn write_ready(&self) -> io::Result<()> {
63        poll_fn(|cx| self.poll_write_ready(cx)).await
64    }
65
66    /// Poll for accept readiness.
67    pub fn poll_accept_ready(&self, cx: &mut Context) -> Poll<io::Result<()>> {
68        self.0.poll_accept_ready(cx)
69    }
70
71    /// Poll for connect readiness.
72    pub fn poll_connect_ready(&self, cx: &mut Context) -> Poll<io::Result<()>> {
73        self.0.poll_connect_ready(cx)
74    }
75
76    /// Poll for read readiness.
77    pub fn poll_read_ready(&self, cx: &mut Context) -> Poll<io::Result<()>> {
78        self.0.poll_read_ready(cx)
79    }
80
81    /// Poll for write readiness.
82    pub fn poll_write_ready(&self, cx: &mut Context) -> Poll<io::Result<()>> {
83        self.0.poll_write_ready(cx)
84    }
85
86    /// Poll for accept readiness and call the provided function.
87    pub fn poll_accept_with<R>(
88        &self,
89        cx: &mut Context,
90        mut f: impl FnMut(&T) -> io::Result<R>,
91    ) -> Poll<io::Result<R>> {
92        loop {
93            match f(&self.0) {
94                Ok(result) => break Poll::Ready(Ok(result)),
95                Err(e) if is_would_block(&e) => {
96                    std::task::ready!(self.poll_accept_ready(cx))?;
97                }
98                Err(e) => break Poll::Ready(Err(e)),
99            }
100        }
101    }
102
103    /// Poll for read readiness and call the provided function.
104    pub fn poll_read_with<R>(
105        &self,
106        cx: &mut Context,
107        mut f: impl FnMut(&T) -> io::Result<R>,
108    ) -> Poll<io::Result<R>> {
109        loop {
110            match f(&self.0) {
111                Ok(result) => break Poll::Ready(Ok(result)),
112                Err(e) if is_would_block(&e) => {
113                    std::task::ready!(self.poll_read_ready(cx))?;
114                }
115                Err(e) => break Poll::Ready(Err(e)),
116            }
117        }
118    }
119
120    /// Poll for write readiness and call the provided function.
121    pub fn poll_write_with<R>(
122        &self,
123        cx: &mut Context,
124        mut f: impl FnMut(&T) -> io::Result<R>,
125    ) -> Poll<io::Result<R>> {
126        loop {
127            match f(&self.0) {
128                Ok(result) => break Poll::Ready(Ok(result)),
129                Err(e) if is_would_block(&e) => {
130                    std::task::ready!(self.poll_write_ready(cx))?;
131                }
132                Err(e) => break Poll::Ready(Err(e)),
133            }
134        }
135    }
136}
137
138impl<T: AsFd + 'static> PollFd<T>
139where
140    for<'a> &'a T: std::io::Read,
141{
142    /// Poll for read readiness and read data.
143    pub fn poll_read(&self, cx: &mut Context, buf: &mut [u8]) -> Poll<io::Result<usize>> {
144        self.poll_read_with(cx, |fd| std::io::Read::read(&mut &*fd, buf))
145    }
146
147    /// Poll for read readiness and read data into an uninitialized buffer.
148    #[cfg(feature = "read_buf")]
149    pub fn poll_read_buf(
150        &self,
151        cx: &mut Context,
152        mut buf: std::io::BorrowedCursor<u8>,
153    ) -> Poll<io::Result<()>> {
154        self.poll_read_with(cx, |fd| std::io::Read::read_buf(&mut &*fd, buf.reborrow()))
155    }
156}
157
158impl<T: AsFd + 'static> PollFd<T>
159where
160    for<'a> &'a T: std::io::Write,
161{
162    /// Poll for write readiness and write data.
163    pub fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
164        self.poll_write_with(cx, |fd| std::io::Write::write(&mut &*fd, buf))
165    }
166
167    /// Poll for write readiness and write data from a slice of buffers.
168    ///
169    /// Whether this is more efficient than [`poll_write`] depends on the
170    /// source: it is a single `writev` for sockets and files, while other
171    /// sources may fall back to writing the first non-empty buffer.
172    ///
173    /// [`poll_write`]: Self::poll_write
174    pub fn poll_write_vectored(
175        &self,
176        cx: &mut Context<'_>,
177        bufs: &[io::IoSlice<'_>],
178    ) -> Poll<io::Result<usize>> {
179        self.poll_write_with(cx, |fd| std::io::Write::write_vectored(&mut &*fd, bufs))
180    }
181
182    /// Poll for write readiness and flush the source.
183    pub fn poll_flush(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
184        self.poll_write_with(cx, |fd| std::io::Write::flush(&mut &*fd))
185    }
186}
187
188impl<T: AsFd> PollFd<T> {
189    /// Shut down the write half, so the peer of a connected socket observes the
190    /// end of the stream while this side can still read.
191    ///
192    /// Sources that cannot be half-closed report success and are left as they
193    /// are, since there is no write half to shut down. Shutting down twice is
194    /// successful as well.
195    ///
196    /// Like the `shutdown` methods in `std`, this does not flush the source.
197    fn shutdown_write(&self) -> io::Result<()> {
198        match sys::shutdown_write(self.0.as_fd()) {
199            // Not a socket, or a socket that never reached a connected state.
200            Err(e) if is_not_a_connected_socket(&e) => Ok(()),
201            result => result,
202        }
203    }
204}
205
206impl<T: AsFd> IntoInner for PollFd<T> {
207    type Inner = SharedFd<T>;
208
209    fn into_inner(self) -> Self::Inner {
210        self.0.into_inner()
211    }
212}
213
214impl<T: AsFd> ToSharedFd<T> for PollFd<T> {
215    fn to_shared_fd(&self) -> SharedFd<T> {
216        self.0.to_shared_fd()
217    }
218}
219
220impl<T: AsFd> AsFd for PollFd<T> {
221    fn as_fd(&self) -> BorrowedFd<'_> {
222        self.0.as_fd()
223    }
224}
225
226impl<T: AsFd> AsRawFd for PollFd<T> {
227    fn as_raw_fd(&self) -> RawFd {
228        self.0.as_raw_fd()
229    }
230}
231
232#[cfg(windows)]
233impl<T: AsFd + AsRawSocket> AsRawSocket for PollFd<T> {
234    fn as_raw_socket(&self) -> RawSocket {
235        self.0.as_raw_socket()
236    }
237}
238
239impl<T: AsFd> Deref for PollFd<T> {
240    type Target = T;
241
242    fn deref(&self) -> &Self::Target {
243        &self.0
244    }
245}
246
247fn is_would_block(e: &io::Error) -> bool {
248    #[cfg(unix)]
249    {
250        e.kind() == io::ErrorKind::WouldBlock || e.raw_os_error() == Some(libc::EINPROGRESS)
251    }
252    #[cfg(not(unix))]
253    {
254        e.kind() == io::ErrorKind::WouldBlock
255    }
256}
257
258/// Whether the error says that the source is not a socket, or is a socket that
259/// never reached a connected state.
260fn is_not_a_connected_socket(e: &io::Error) -> bool {
261    cfg_select! {
262        unix => {
263            matches!(
264                e.raw_os_error(),
265                Some(libc::ENOTSOCK) | Some(libc::ENOTCONN)
266            )
267        }
268        windows => {
269            use windows_sys::Win32::Networking::WinSock::{WSAENOTCONN, WSAENOTSOCK};
270
271            matches!(e.raw_os_error(), Some(WSAENOTSOCK) | Some(WSAENOTCONN))
272        }
273    }
274}
275
276impl<T: AsFd + 'static> futures_util::AsyncRead for &PollFd<T>
277where
278    for<'a> &'a T: std::io::Read,
279{
280    fn poll_read(
281        self: Pin<&mut Self>,
282        cx: &mut Context<'_>,
283        buf: &mut [u8],
284    ) -> Poll<io::Result<usize>> {
285        (*self).poll_read(cx, buf)
286    }
287}
288
289impl<T: AsFd + 'static> futures_util::AsyncRead for PollFd<T>
290where
291    for<'a> &'a T: std::io::Read,
292{
293    fn poll_read(
294        self: Pin<&mut Self>,
295        cx: &mut Context<'_>,
296        buf: &mut [u8],
297    ) -> Poll<io::Result<usize>> {
298        (*self).poll_read(cx, buf)
299    }
300}
301
302impl<T: AsFd + 'static> futures_util::AsyncWrite for &PollFd<T>
303where
304    for<'a> &'a T: std::io::Write,
305{
306    fn poll_write(
307        self: Pin<&mut Self>,
308        cx: &mut Context<'_>,
309        buf: &[u8],
310    ) -> Poll<io::Result<usize>> {
311        (*self).poll_write(cx, buf)
312    }
313
314    fn poll_write_vectored(
315        self: Pin<&mut Self>,
316        cx: &mut Context<'_>,
317        bufs: &[io::IoSlice<'_>],
318    ) -> Poll<io::Result<usize>> {
319        (*self).poll_write_vectored(cx, bufs)
320    }
321
322    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
323        (*self).poll_flush(cx)
324    }
325
326    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
327        // Deliberately not flushing: the write readiness registration is shared
328        // with any pending write, so flushing here would steal its waker.
329        Poll::Ready(self.shutdown_write())
330    }
331}
332
333impl<T: AsFd + 'static> futures_util::AsyncWrite for PollFd<T>
334where
335    for<'a> &'a T: std::io::Write,
336{
337    fn poll_write(
338        self: Pin<&mut Self>,
339        cx: &mut Context<'_>,
340        buf: &[u8],
341    ) -> Poll<io::Result<usize>> {
342        (*self).poll_write(cx, buf)
343    }
344
345    fn poll_write_vectored(
346        self: Pin<&mut Self>,
347        cx: &mut Context<'_>,
348        bufs: &[io::IoSlice<'_>],
349    ) -> Poll<io::Result<usize>> {
350        (*self).poll_write_vectored(cx, bufs)
351    }
352
353    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
354        (*self).poll_flush(cx)
355    }
356
357    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
358        // Deliberately not flushing: the write readiness registration is shared
359        // with any pending write, so flushing here would steal its waker.
360        Poll::Ready(self.shutdown_write())
361    }
362}