1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use core::cell::UnsafeCell;

use crate::result::{Error, Result};

/// A request for events related to a particular file descriptor in a call to
/// [`poll`].
#[repr(C)]
pub struct PollRequest<'a> {
    // Note: The layout of this struct must exactly match the kernel's
    // struct pollfd, because we'll be passing a pointer to an array of
    // instances of this struct directly to the kernel.
    fd: linux_unsafe::int,
    events: linux_unsafe::short,
    revents: UnsafeCell<linux_unsafe::short>, // Kernel will write in here
    _phantom: core::marker::PhantomData<&'a super::File>,
}

impl<'a> PollRequest<'a> {
    /// Begin constructing a [`PollRequest`] that describes events to request
    /// for the given file to use in a subsequent call to [`poll`].
    ///
    /// The the given file must outlive the returned poll request.
    #[inline]
    pub const fn new<'f: 'a>(file: &'f super::File) -> Self {
        let fd = file.fd;
        Self {
            fd,
            events: 0,
            revents: UnsafeCell::new(0),
            _phantom: core::marker::PhantomData,
        }
    }

    /// Directly set the events bitmask for this request.
    ///
    /// Safety: The given bitmask must be a valid value for the `events`
    /// field of `struct pollfd` in the kernel's C API.
    #[inline(always)]
    pub unsafe fn events_raw(mut self, bitmask: core::ffi::c_short) -> Self {
        self.events = bitmask;
        self
    }

    /// Merge the given events bitmask with the existing event bits in the
    /// object using the bitwise OR operation.
    ///
    /// Safety: The given bitmask must be a valid value for the `events`
    /// field of `struct pollfd` in the kernel's C API.
    #[inline(always)]
    pub unsafe fn or_events_raw(mut self, bitmask: core::ffi::c_short) -> Self {
        self.events |= bitmask;
        self
    }

    /// Returns the result written by the latest call to [`poll`] that included
    /// this poll request object. If [`poll`] hasn't yet been called then the
    /// response indicates that no events have occurred.
    #[inline(always)]
    pub fn response(&self) -> PollResponse {
        PollResponse::new(unsafe { *self.revents.get() })
    }
}

/// Represents the bitmask of event flags produced for a [`PollRequest`] when
/// passed to [`poll`].
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct PollResponse {
    revents: core::ffi::c_short,
}

impl PollResponse {
    #[inline(always)]
    const fn new(raw: core::ffi::c_short) -> Self {
        Self { revents: raw }
    }

    /// Returns the raw bitmask representing the events that the kernel
    /// reported. The meaning of this bitmask may be architecture-dependent.
    #[inline(always)]
    pub const fn raw_result(&self) -> core::ffi::c_short {
        self.revents
    }

    /// Returns true if the response indicates that there is data to read.
    #[inline(always)]
    pub const fn readable(&self) -> bool {
        (self.revents & linux_unsafe::POLLIN) != 0
    }

    /// Returns true if the response indicates that the file is writable.
    ///
    /// This only indicates that _some_ amount of writing is possible, but
    /// does not guarantee that a write of any given size will succeed.
    #[inline(always)]
    pub const fn writable(&self) -> bool {
        (self.revents & linux_unsafe::POLLOUT) != 0
    }

    /// Returns true if there is an error condition on the file descriptor.
    ///
    /// This condition is also used for the write end of a pipe once the read
    /// end has been closed.
    #[inline(always)]
    pub const fn error(&self) -> bool {
        (self.revents & linux_unsafe::POLLERR) != 0
    }

    /// Returns true if the other end of a channel has been closed.
    ///
    /// There might still be data in the read buffer, which can be read until
    /// reaching EOF.
    #[inline(always)]
    pub const fn hung_up(&self) -> bool {
        (self.revents & linux_unsafe::POLLERR) != 0
    }

    /// Returns true if there is an exceptional condition on the file descriptor.
    #[inline(always)]
    pub const fn exception(&self) -> bool {
        (self.revents & linux_unsafe::POLLPRI) != 0
    }

    /// Returns true if the kernel deemed the corresponding request to be invalid.
    #[inline(always)]
    pub const fn invalid(&self) -> bool {
        (self.revents & linux_unsafe::POLLNVAL) != 0
    }
}

/// `poll` wraps the Linux system call of the same name, passing all of the
/// given requests to the kernel.
///
/// *Warning:* This abstraction doesn't really work because the lifetime
/// bounds on `PollRequest` make the `File` objects unusable once the are
/// used here with a mutable borrow. We'll hopefully fix this in a future
/// release.
///
/// The kernel will modify the request objects in-place with updated event
/// flags, which you can then retrieve using [`PollRequest::response`].
/// The successful return value is the number of entries in `reqs` that now
/// have at least one response flag set.
///
/// The kernel may have an upper limit on the number of entries in reqs that
/// is smaller than the maximum value of `usize`. If the given slice is too
/// long then this function will return the EINVAL error code.
pub fn poll(reqs: &mut [PollRequest], timeout: linux_unsafe::int) -> Result<linux_unsafe::int> {
    // NOTE: We're effectively transmuting our PollRequest type into
    // the kernel's struct pollfd here. This is safe because the layout
    // of our struct should exactly match the kernel's, and the kernel
    // will only write into our "revents" field, and any bits written
    // in there are valid for type "short".
    let reqs_ptr = reqs.as_ptr() as *mut linux_unsafe::pollfd;
    if reqs.len() > (!(0 as linux_unsafe::nfds_t)) as usize {
        // More file descriptors than the kernel can physicall support on this
        // platform, so we'll return a synthetic EINVAL to mimic how the
        // kernel would behave if it had a smaller soft limit.
        return Err(Error::new(22)); // hard-coded EINVAL value (TODO: expose this as a constant from linux-unsafe instead?)
    }
    let reqs_count = reqs.len() as linux_unsafe::nfds_t;
    let result = unsafe { linux_unsafe::poll(reqs_ptr, reqs_count, timeout) };
    result.map(|count| count as _).map_err(|e| e.into())
}