linux_io/
poll.rs

1use core::{cell::UnsafeCell, ptr::null};
2
3use crate::result::{Error, Result};
4
5/// A request for events related to a particular file descriptor in a call to
6/// [`poll`].
7#[repr(C)]
8pub struct PollRequest<'a> {
9    // Note: The layout of this struct must exactly match the kernel's
10    // struct pollfd, because we'll be passing a pointer to an array of
11    // instances of this struct directly to the kernel.
12    fd: linux_unsafe::int,
13    events: linux_unsafe::short,
14    revents: UnsafeCell<linux_unsafe::short>, // Kernel will write in here
15    _phantom: core::marker::PhantomData<&'a super::File>,
16}
17
18impl<'a> PollRequest<'a> {
19    /// Begin constructing a [`PollRequest`] that describes events to request
20    /// for the given file to use in a subsequent call to [`poll`].
21    ///
22    /// The the given file must outlive the returned poll request.
23    #[inline]
24    pub const fn new<'f: 'a>(file: &'f super::File) -> Self {
25        let fd = file.fd;
26        Self {
27            fd,
28            events: 0,
29            revents: UnsafeCell::new(0),
30            _phantom: core::marker::PhantomData,
31        }
32    }
33
34    /// Directly set the events bitmask for this request.
35    ///
36    /// Safety: The given bitmask must be a valid value for the `events`
37    /// field of `struct pollfd` in the kernel's C API.
38    #[inline(always)]
39    pub unsafe fn events_raw(mut self, bitmask: core::ffi::c_short) -> Self {
40        self.events = bitmask;
41        self
42    }
43
44    /// Merge the given events bitmask with the existing event bits in the
45    /// object using the bitwise OR operation.
46    ///
47    /// Safety: The given bitmask must be a valid value for the `events`
48    /// field of `struct pollfd` in the kernel's C API.
49    #[inline(always)]
50    pub unsafe fn or_events_raw(mut self, bitmask: core::ffi::c_short) -> Self {
51        self.events |= bitmask;
52        self
53    }
54
55    /// Returns the result written by the latest call to [`poll`] that included
56    /// this poll request object. If [`poll`] hasn't yet been called then the
57    /// response indicates that no events have occurred.
58    #[inline(always)]
59    pub fn response(&self) -> PollResponse {
60        PollResponse::new(unsafe { *self.revents.get() })
61    }
62}
63
64/// Represents the bitmask of event flags produced for a [`PollRequest`] when
65/// passed to [`poll`].
66#[derive(Clone, Copy, Debug)]
67#[repr(transparent)]
68pub struct PollResponse {
69    revents: core::ffi::c_short,
70}
71
72impl PollResponse {
73    #[inline(always)]
74    const fn new(raw: core::ffi::c_short) -> Self {
75        Self { revents: raw }
76    }
77
78    /// Returns the raw bitmask representing the events that the kernel
79    /// reported. The meaning of this bitmask may be architecture-dependent.
80    #[inline(always)]
81    pub const fn raw_result(&self) -> core::ffi::c_short {
82        self.revents
83    }
84
85    /// Returns true if the response indicates that there is data to read.
86    #[inline(always)]
87    pub const fn readable(&self) -> bool {
88        (self.revents & linux_unsafe::POLLIN) != 0
89    }
90
91    /// Returns true if the response indicates that the file is writable.
92    ///
93    /// This only indicates that _some_ amount of writing is possible, but
94    /// does not guarantee that a write of any given size will succeed.
95    #[inline(always)]
96    pub const fn writable(&self) -> bool {
97        (self.revents & linux_unsafe::POLLOUT) != 0
98    }
99
100    /// Returns true if there is an error condition on the file descriptor.
101    ///
102    /// This condition is also used for the write end of a pipe once the read
103    /// end has been closed.
104    #[inline(always)]
105    pub const fn error(&self) -> bool {
106        (self.revents & linux_unsafe::POLLERR) != 0
107    }
108
109    /// Returns true if the other end of a channel has been closed.
110    ///
111    /// There might still be data in the read buffer, which can be read until
112    /// reaching EOF.
113    #[inline(always)]
114    pub const fn hung_up(&self) -> bool {
115        (self.revents & linux_unsafe::POLLERR) != 0
116    }
117
118    /// Returns true if there is an exceptional condition on the file descriptor.
119    #[inline(always)]
120    pub const fn exception(&self) -> bool {
121        (self.revents & linux_unsafe::POLLPRI) != 0
122    }
123
124    /// Returns true if the kernel deemed the corresponding request to be invalid.
125    #[inline(always)]
126    pub const fn invalid(&self) -> bool {
127        (self.revents & linux_unsafe::POLLNVAL) != 0
128    }
129}
130
131/// `poll` wraps the Linux system call of the same name, or at least one
132/// with a similar name, passing all of the given requests to the kernel.
133///
134/// *Warning:* This abstraction doesn't really work because the lifetime
135/// bounds on `PollRequest` make the `File` objects unusable once the are
136/// used here with a mutable borrow. We'll hopefully fix this in a future
137/// release.
138///
139/// The kernel will modify the request objects in-place with updated event
140/// flags, which you can then retrieve using [`PollRequest::response`].
141/// The successful return value is the number of entries in `reqs` that now
142/// have at least one response flag set.
143///
144/// The kernel may have an upper limit on the number of entries in reqs that
145/// is smaller than the maximum value of `usize`. If the given slice is too
146/// long then this function will return the EINVAL error code.
147pub fn poll(reqs: &mut [PollRequest], timeout: linux_unsafe::int) -> Result<linux_unsafe::int> {
148    // NOTE: We're effectively transmuting our PollRequest type into
149    // the kernel's struct pollfd here. This is safe because the layout
150    // of our struct should exactly match the kernel's, and the kernel
151    // will only write into our "revents" field, and any bits written
152    // in there are valid for type "short".
153    let reqs_ptr = reqs.as_ptr() as *mut linux_unsafe::pollfd;
154    if reqs.len() > (!(0 as linux_unsafe::nfds_t)) as usize {
155        // More file descriptors than the kernel can physicall support on this
156        // platform, so we'll return a synthetic EINVAL to mimic how the
157        // kernel would behave if it had a smaller soft limit.
158        return Err(Error::new(22)); // hard-coded EINVAL value (TODO: expose this as a constant from linux-unsafe instead?)
159    }
160    let reqs_count = reqs.len() as linux_unsafe::nfds_t;
161    // We actually use ppoll rather than poll, because poll is not
162    // available on recently-added architectures like riscv64.
163    let tmo = linux_unsafe::timespec {
164        tv_sec: (timeout / 1000) as linux_unsafe::long,
165        tv_nsec: ((timeout % 1000) * 1_000_000) as linux_unsafe::long,
166    };
167    let tmo_p = &tmo as *const _;
168    let result = unsafe { linux_unsafe::ppoll(reqs_ptr, reqs_count, tmo_p, null()) };
169    result.map(|count| count as _).map_err(|e| e.into())
170}