Skip to main content

jay_config/
io.rs

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