jay_config/
io.rs

1//! Tools for IO operations.
2
3use {
4    crate::_private::PollableId,
5    futures_util::{io::AsyncRead, AsyncWrite},
6    std::{
7        future::poll_fn,
8        io::{self, ErrorKind, IoSlice, IoSliceMut, Read, Write},
9        os::fd::{AsFd, AsRawFd},
10        pin::Pin,
11        task::{ready, Context, Poll},
12    },
13    thiserror::Error,
14    uapi::c,
15};
16
17#[derive(Debug, Error)]
18enum AsyncError {
19    #[error("Could not retrieve the file description flags")]
20    GetFl(#[source] io::Error),
21    #[error("Could not set the file description flags")]
22    SetFl(#[source] io::Error),
23    #[error("This configuration has already been destroyed")]
24    Destroyed,
25    #[error("The compositor could not create the necessary data structures: {0}")]
26    CompositorSetup(String),
27    #[error("Could not poll the file description: {0}")]
28    Poll(String),
29}
30
31impl From<AsyncError> for io::Error {
32    fn from(value: AsyncError) -> Self {
33        io::Error::new(ErrorKind::Other, value)
34    }
35}
36
37/// An async adapter for types implementing [`AsFd`].
38pub struct Async<T> {
39    id: PollableIdWrapper,
40    t: Option<T>,
41}
42
43impl<T> Unpin for Async<T> {}
44
45struct PollableIdWrapper {
46    id: PollableId,
47}
48
49impl Drop for PollableIdWrapper {
50    fn drop(&mut self) {
51        get!().remove_pollable(self.id);
52    }
53}
54
55impl<T> Async<T>
56where
57    T: AsFd,
58{
59    /// Creates a new async adapter.
60    ///
61    /// This takes ownership of the file description and duplicates the file descriptor.
62    /// You should not modify the file description while this object is in use, otherwise
63    /// the behavior is undefined.
64    pub fn new(t: T) -> Result<Self, io::Error> {
65        Ok(Self::new_(t)?)
66    }
67
68    fn new_(t: T) -> Result<Self, AsyncError> {
69        let fd = t.as_fd();
70        let fl = uapi::fcntl_getfl(fd.as_raw_fd())
71            .map_err(|e| AsyncError::GetFl(io::Error::from_raw_os_error(e.0)))?;
72        uapi::fcntl_setfl(fd.as_raw_fd(), fl | c::O_NONBLOCK)
73            .map_err(|e| AsyncError::SetFl(io::Error::from_raw_os_error(e.0)))?;
74        let id = get!(Err(AsyncError::Destroyed))
75            .create_pollable(fd.as_raw_fd())
76            .map_err(AsyncError::CompositorSetup)?;
77        Ok(Self {
78            id: PollableIdWrapper { id },
79            t: Some(t),
80        })
81    }
82}
83
84impl<T> Async<T> {
85    /// Unwraps the underlying object.
86    ///
87    /// Note that the underlying object is still non-blocking at this point.
88    pub fn unwrap(self) -> T {
89        self.t.unwrap()
90    }
91
92    fn poll_(&self, writable: bool, cx: &mut Context<'_>) -> Poll<Result<(), AsyncError>> {
93        get!(Poll::Ready(Err(AsyncError::Destroyed)))
94            .poll_io(self.id.id, writable, cx)
95            .map_err(AsyncError::Poll)
96    }
97
98    async fn poll(&self, writable: bool) -> Result<(), io::Error> {
99        poll_fn(|cx| self.poll_(writable, cx)).await?;
100        Ok(())
101    }
102
103    /// Waits for the file description to become readable.
104    pub async fn readable(&self) -> Result<(), io::Error> {
105        self.poll(false).await
106    }
107
108    /// Waits for the file description to become writable.
109    pub async fn writable(&self) -> Result<(), io::Error> {
110        self.poll(true).await
111    }
112}
113
114impl<T> AsRef<T> for Async<T> {
115    fn as_ref(&self) -> &T {
116        self.t.as_ref().unwrap()
117    }
118}
119
120impl<T> AsMut<T> for Async<T> {
121    fn as_mut(&mut self) -> &mut T {
122        self.t.as_mut().unwrap()
123    }
124}
125
126fn poll_io<T, R>(
127    slf: &mut Async<T>,
128    writable: bool,
129    cx: &mut Context<'_>,
130    mut f: impl FnMut(&mut Async<T>) -> io::Result<R>,
131) -> Poll<io::Result<R>> {
132    loop {
133        ready!(slf.poll_(writable, cx))?;
134        match f(slf) {
135            Err(e) if e.kind() == ErrorKind::WouldBlock => {}
136            res => return Poll::Ready(res),
137        }
138    }
139}
140
141impl<T> AsyncRead for Async<T>
142where
143    T: Read,
144{
145    fn poll_read(
146        self: Pin<&mut Self>,
147        cx: &mut Context<'_>,
148        buf: &mut [u8],
149    ) -> Poll<io::Result<usize>> {
150        poll_io(self.get_mut(), false, cx, |slf| slf.as_mut().read(buf))
151    }
152
153    fn poll_read_vectored(
154        self: Pin<&mut Self>,
155        cx: &mut Context<'_>,
156        bufs: &mut [IoSliceMut<'_>],
157    ) -> Poll<io::Result<usize>> {
158        poll_io(self.get_mut(), false, cx, |slf| {
159            slf.as_mut().read_vectored(bufs)
160        })
161    }
162}
163
164impl<T> AsyncWrite for Async<T>
165where
166    T: Write,
167{
168    fn poll_write(
169        self: Pin<&mut Self>,
170        cx: &mut Context<'_>,
171        buf: &[u8],
172    ) -> Poll<io::Result<usize>> {
173        poll_io(self.get_mut(), true, cx, |slf| slf.as_mut().write(buf))
174    }
175
176    fn poll_write_vectored(
177        self: Pin<&mut Self>,
178        cx: &mut Context<'_>,
179        bufs: &[IoSlice<'_>],
180    ) -> Poll<io::Result<usize>> {
181        poll_io(self.get_mut(), true, cx, |slf| {
182            slf.as_mut().write_vectored(bufs)
183        })
184    }
185
186    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
187        poll_io(self.get_mut(), true, cx, |slf| slf.as_mut().flush())
188    }
189
190    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
191        self.get_mut().t.take();
192        Poll::Ready(Ok(()))
193    }
194}