Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
159
third-party/vendor/calloop/src/sys/epoll.rs
vendored
Normal file
159
third-party/vendor/calloop/src/sys/epoll.rs
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::{Interest, Mode, PollEvent, Readiness, Token};
|
||||
|
||||
use nix::sys::{
|
||||
epoll::{
|
||||
epoll_create1, epoll_ctl, epoll_wait, EpollCreateFlags, EpollEvent, EpollFlags, EpollOp,
|
||||
},
|
||||
time::TimeSpec,
|
||||
timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags},
|
||||
};
|
||||
|
||||
pub struct Epoll {
|
||||
epoll_fd: RawFd,
|
||||
timer_fd: Option<TimerFd>,
|
||||
}
|
||||
|
||||
const TIMER_DATA: u64 = u64::MAX;
|
||||
|
||||
fn make_flags(interest: Interest, mode: Mode) -> EpollFlags {
|
||||
let mut flags = EpollFlags::empty();
|
||||
if interest.readable {
|
||||
flags |= EpollFlags::EPOLLIN;
|
||||
}
|
||||
if interest.writable {
|
||||
flags |= EpollFlags::EPOLLOUT;
|
||||
}
|
||||
match mode {
|
||||
Mode::Level => { /* This is the default */ }
|
||||
Mode::Edge => flags |= EpollFlags::EPOLLET,
|
||||
Mode::OneShot => flags |= EpollFlags::EPOLLONESHOT,
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
fn flags_to_readiness(flags: EpollFlags) -> Readiness {
|
||||
Readiness {
|
||||
readable: flags.contains(EpollFlags::EPOLLIN),
|
||||
writable: flags.contains(EpollFlags::EPOLLOUT),
|
||||
error: flags.contains(EpollFlags::EPOLLERR),
|
||||
}
|
||||
}
|
||||
|
||||
impl Epoll {
|
||||
pub(crate) fn new(high_precision: bool) -> crate::Result<Epoll> {
|
||||
let epoll_fd = epoll_create1(EpollCreateFlags::EPOLL_CLOEXEC)?;
|
||||
let mut timer_fd = None;
|
||||
if high_precision {
|
||||
// Prepare a timerfd for precise time tracking and register it to the event queue
|
||||
// This timerfd allows for nanosecond precision in setting the timout up (though in practice
|
||||
// we rather get ~10 microsecond precision), while epoll_wait() API only allows millisecond
|
||||
// granularity
|
||||
let timer = TimerFd::new(
|
||||
ClockId::CLOCK_MONOTONIC,
|
||||
TimerFlags::TFD_CLOEXEC | TimerFlags::TFD_NONBLOCK,
|
||||
)?;
|
||||
let mut timer_event = EpollEvent::new(EpollFlags::EPOLLIN, TIMER_DATA);
|
||||
epoll_ctl(
|
||||
epoll_fd,
|
||||
EpollOp::EpollCtlAdd,
|
||||
timer.as_raw_fd(),
|
||||
&mut timer_event,
|
||||
)?;
|
||||
timer_fd = Some(timer);
|
||||
}
|
||||
Ok(Epoll { epoll_fd, timer_fd })
|
||||
}
|
||||
|
||||
pub(crate) fn poll(
|
||||
&mut self,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> crate::Result<Vec<PollEvent>> {
|
||||
let mut buffer = [EpollEvent::empty(); 32];
|
||||
if let Some(ref timer) = self.timer_fd {
|
||||
if let Some(timeout) = timeout {
|
||||
// Set up the precise timer
|
||||
timer.set(
|
||||
Expiration::OneShot(TimeSpec::from_duration(timeout)),
|
||||
TimerSetTimeFlags::empty(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
// add 1 to the millisecond wait, to round up for timer tracking. If the high precision timer is set up
|
||||
// it'll fire before that timeout
|
||||
let timeout = timeout.map(|d| (d.as_millis() + 1) as isize).unwrap_or(-1);
|
||||
let n_ready = epoll_wait(self.epoll_fd, &mut buffer, timeout)?;
|
||||
let events = buffer
|
||||
.iter()
|
||||
.take(n_ready)
|
||||
.flat_map(|event| {
|
||||
if event.data() == TIMER_DATA {
|
||||
// We woke up because the high-precision timer fired, we need to disarm it by reading its
|
||||
// contents to ensure it will be ready for next time
|
||||
// Timer is created in non-blocking mode, and should have already fired anyway, this
|
||||
// cannot possibly block
|
||||
let _ = self
|
||||
.timer_fd
|
||||
.as_ref()
|
||||
.expect("Got an event from high-precision timer while it is not set up?!")
|
||||
.wait();
|
||||
// don't forward this event to downstream
|
||||
None
|
||||
} else {
|
||||
// In C, the underlying data type is a union including a void
|
||||
// pointer; in Rust's FFI bindings, it only exposes the u64. The
|
||||
// round-trip conversion is valid however.
|
||||
let token_ptr = event.data() as usize as *const Token;
|
||||
Some(PollEvent {
|
||||
readiness: flags_to_readiness(event.events()),
|
||||
// Why this is safe: it points to memory boxed and owned by
|
||||
// the parent Poller type.
|
||||
token: unsafe { *token_ptr },
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if let Some(ref timer) = self.timer_fd {
|
||||
// in all cases, disarm the timer
|
||||
timer.unset()?;
|
||||
// clear the timer in case it fired between epoll_wait and now, as timer is in
|
||||
// non-blocking mode, this will return Err(WouldBlock) if it had not fired, so
|
||||
// we ignore the error
|
||||
let _ = timer.wait();
|
||||
}
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64);
|
||||
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlAdd, fd, &mut event).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn reregister(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64);
|
||||
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlMod, fd, &mut event).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
|
||||
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlDel, fd, None).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Epoll {
|
||||
fn drop(&mut self) {
|
||||
let _ = nix::unistd::close(self.epoll_fd);
|
||||
}
|
||||
}
|
||||
223
third-party/vendor/calloop/src/sys/kqueue.rs
vendored
Normal file
223
third-party/vendor/calloop/src/sys/kqueue.rs
vendored
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
use std::{io, os::unix::io::RawFd};
|
||||
|
||||
use nix::{
|
||||
libc::{c_long, time_t, timespec},
|
||||
sys::event::{kevent_ts, kqueue, EventFilter, EventFlag, FilterFlag, KEvent},
|
||||
};
|
||||
|
||||
use super::{Interest, Mode, PollEvent, Readiness, Token};
|
||||
|
||||
pub struct Kqueue {
|
||||
kq: RawFd,
|
||||
}
|
||||
|
||||
fn mode_to_flag(mode: Mode) -> EventFlag {
|
||||
match mode {
|
||||
Mode::Level => EventFlag::empty(),
|
||||
Mode::OneShot => EventFlag::EV_DISPATCH,
|
||||
Mode::Edge => EventFlag::EV_CLEAR,
|
||||
}
|
||||
}
|
||||
|
||||
impl Kqueue {
|
||||
// Kqueue is always high precision
|
||||
pub(crate) fn new(_high_precision: bool) -> crate::Result<Kqueue> {
|
||||
let kq = kqueue()?;
|
||||
Ok(Kqueue { kq })
|
||||
}
|
||||
|
||||
pub(crate) fn poll(
|
||||
&mut self,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> crate::Result<Vec<PollEvent>> {
|
||||
let mut buffer = [KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
); 32];
|
||||
|
||||
let nevents = kevent_ts(
|
||||
self.kq,
|
||||
&[],
|
||||
&mut buffer,
|
||||
timeout.map(|d| timespec {
|
||||
tv_sec: d.as_secs() as time_t,
|
||||
tv_nsec: d.subsec_nanos() as c_long,
|
||||
}),
|
||||
)?;
|
||||
|
||||
let ret = buffer
|
||||
.iter()
|
||||
.take(nevents)
|
||||
.map(|event| {
|
||||
// The kevent data field in Rust's libc FFI bindings is an
|
||||
// intptr_t, which is specified to allow this kind of
|
||||
// conversion.
|
||||
let token_ptr = event.udata() as usize as *const Token;
|
||||
PollEvent {
|
||||
readiness: Readiness {
|
||||
readable: event.filter() == Ok(EventFilter::EVFILT_READ),
|
||||
writable: event.filter() == Ok(EventFilter::EVFILT_WRITE),
|
||||
error: event.flags().contains(EventFlag::EV_ERROR) && event.data() != 0,
|
||||
},
|
||||
// Why this is safe: it points to memory boxed and owned by
|
||||
// the parent Poller type.
|
||||
token: unsafe { *token_ptr },
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
self.reregister(fd, interest, mode, token)
|
||||
}
|
||||
|
||||
pub fn reregister(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: *const Token,
|
||||
) -> crate::Result<()> {
|
||||
let write_flags = if interest.writable {
|
||||
EventFlag::EV_ADD | EventFlag::EV_RECEIPT | mode_to_flag(mode)
|
||||
} else {
|
||||
EventFlag::EV_DELETE
|
||||
};
|
||||
let read_flags = if interest.readable {
|
||||
EventFlag::EV_ADD | EventFlag::EV_RECEIPT | mode_to_flag(mode)
|
||||
} else {
|
||||
EventFlag::EV_DELETE
|
||||
};
|
||||
|
||||
let changes = [
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
write_flags,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
token as usize as isize,
|
||||
),
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_READ,
|
||||
read_flags,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
token as usize as isize,
|
||||
),
|
||||
];
|
||||
|
||||
let mut out = [
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
kevent_ts(self.kq, &changes, &mut out, None)?;
|
||||
|
||||
for o in &out {
|
||||
if o.flags().contains(EventFlag::EV_ERROR) && o.data() != 0 {
|
||||
let e = io::Error::from_raw_os_error(o.data() as i32);
|
||||
// ignore NotFound error which is raised if we tried to remove a non-existent filter
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
|
||||
let changes = [
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
EventFlag::EV_DELETE,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
KEvent::new(
|
||||
fd as usize,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::EV_DELETE,
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
let mut out = [
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_WRITE,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
KEvent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
kevent_ts(self.kq, &changes, &mut out, None)?;
|
||||
|
||||
// Report an error if *both* fd were missing, meaning we were not registered at all
|
||||
let mut notfound = 0;
|
||||
|
||||
for o in &out {
|
||||
if o.flags().contains(EventFlag::EV_ERROR) && o.data() != 0 {
|
||||
let e = io::Error::from_raw_os_error(o.data() as i32);
|
||||
// ignore NotFound error which is raised if we tried to remove a non-existent filter
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e.into());
|
||||
} else {
|
||||
notfound += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notfound == 2 {
|
||||
return Err(std::io::Error::from(io::ErrorKind::NotFound).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Kqueue {
|
||||
fn drop(&mut self) {
|
||||
let _ = nix::unistd::close(self.kq);
|
||||
}
|
||||
}
|
||||
363
third-party/vendor/calloop/src/sys/mod.rs
vendored
Normal file
363
third-party/vendor/calloop/src/sys/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
use std::{cell::RefCell, convert::TryInto, os::unix::io::RawFd, rc::Rc, time::Duration};
|
||||
use vec_map::VecMap;
|
||||
|
||||
use crate::{loop_logic::CalloopKey, sources::timer::TimerWheel};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mod epoll;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use epoll::Epoll as Poller;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "macos"
|
||||
))]
|
||||
mod kqueue;
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "macos"
|
||||
))]
|
||||
use kqueue::Kqueue as Poller;
|
||||
|
||||
/// Possible modes for registering a file descriptor
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Mode {
|
||||
/// Single event generation
|
||||
///
|
||||
/// This FD will be disabled as soon as it has generated one event.
|
||||
///
|
||||
/// The user will need to use `LoopHandle::update()` to re-enable it if
|
||||
/// desired.
|
||||
OneShot,
|
||||
/// Level-triggering
|
||||
///
|
||||
/// This FD will report events on every poll as long as the requested interests
|
||||
/// are available. If the same FD is inserted in multiple event loops, all of
|
||||
/// them are notified of readiness.
|
||||
Level,
|
||||
/// Edge-triggering
|
||||
///
|
||||
/// This FD will report events only when it *gains* one of the requested interests.
|
||||
/// it must thus be fully processed before it'll generate events again. If the same
|
||||
/// FD is inserted on multiple event loops, it may be that not all of them are notified
|
||||
/// of readiness, and not necessarily always the same(s) (at least one is notified).
|
||||
Edge,
|
||||
}
|
||||
|
||||
/// Interest to register regarding the file descriptor
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Interest {
|
||||
/// Wait for the FD to be readable
|
||||
pub readable: bool,
|
||||
/// Wait for the FD to be writable
|
||||
pub writable: bool,
|
||||
}
|
||||
|
||||
impl Interest {
|
||||
/// Shorthand for empty interest
|
||||
pub const EMPTY: Interest = Interest {
|
||||
readable: false,
|
||||
writable: false,
|
||||
};
|
||||
/// Shorthand for read interest
|
||||
pub const READ: Interest = Interest {
|
||||
readable: true,
|
||||
writable: false,
|
||||
};
|
||||
/// Shorthand for write interest
|
||||
pub const WRITE: Interest = Interest {
|
||||
readable: false,
|
||||
writable: true,
|
||||
};
|
||||
/// Shorthand for read and write interest
|
||||
pub const BOTH: Interest = Interest {
|
||||
readable: true,
|
||||
writable: true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Readiness for a file descriptor notification
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Readiness {
|
||||
/// Is the FD readable
|
||||
pub readable: bool,
|
||||
/// Is the FD writable
|
||||
pub writable: bool,
|
||||
/// Is the FD in an error state
|
||||
pub error: bool,
|
||||
}
|
||||
|
||||
impl Readiness {
|
||||
/// Shorthand for empty readiness
|
||||
pub const EMPTY: Readiness = Readiness {
|
||||
readable: false,
|
||||
writable: false,
|
||||
error: false,
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PollEvent {
|
||||
pub(crate) readiness: Readiness,
|
||||
pub(crate) token: Token,
|
||||
}
|
||||
|
||||
/// Factory for creating tokens in your registrations
|
||||
///
|
||||
/// When composing event sources, each sub-source needs to
|
||||
/// have its own token to identify itself. This factory is
|
||||
/// provided to produce such unique tokens.
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TokenFactory {
|
||||
key: CalloopKey,
|
||||
sub_id: u32,
|
||||
}
|
||||
|
||||
impl TokenFactory {
|
||||
pub(crate) fn new(key: CalloopKey) -> TokenFactory {
|
||||
TokenFactory { key, sub_id: 0 }
|
||||
}
|
||||
|
||||
/// Produce a new unique token
|
||||
pub fn token(&mut self) -> Token {
|
||||
let token = Token {
|
||||
key: self.key,
|
||||
sub_id: self.sub_id,
|
||||
};
|
||||
self.sub_id += 1;
|
||||
token
|
||||
}
|
||||
}
|
||||
|
||||
/// A token (for implementation of the [`EventSource`](crate::EventSource) trait)
|
||||
///
|
||||
/// This token is produced by the [`TokenFactory`] and is used when calling the
|
||||
/// [`EventSource`](crate::EventSource) implementations to process event, in order
|
||||
/// to identify which sub-source produced them.
|
||||
///
|
||||
/// You should forward it to the [`Poll`] when registering your file descriptors.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Token {
|
||||
pub(crate) key: CalloopKey,
|
||||
pub(crate) sub_id: u32,
|
||||
}
|
||||
|
||||
/// The polling system
|
||||
///
|
||||
/// This type represents the polling system of calloop, on which you
|
||||
/// can register your file descriptors. This interface is only accessible in
|
||||
/// implementations of the [`EventSource`](crate::EventSource) trait.
|
||||
///
|
||||
/// You only need to interact with this type if you are implementing your
|
||||
/// own event sources, while implementing the [`EventSource`](crate::EventSource) trait.
|
||||
/// And even in this case, you can often just use the [`Generic`](crate::generic::Generic) event
|
||||
/// source and delegate the implementations to it.
|
||||
pub struct Poll {
|
||||
poller: Poller,
|
||||
|
||||
// It is essential for safe use of this type that the pointers passed in to
|
||||
// the underlying poller API are properly managed. Each time an event source
|
||||
// is registered, the token it passes in is Boxed and converted to a raw
|
||||
// pointer to be passed to the polling system by FFI. This pointer is what's
|
||||
// stored in the map. When the event source is re- or unregistered, the same
|
||||
// raw pointer can then be converted back into the Box and dropped, safely
|
||||
// deallocating it. To put it another way, we effectively "own" the Token
|
||||
// memory on behalf of the underlying polling mechanism.
|
||||
//
|
||||
// All the platforms we currently support follow the rule that file
|
||||
// descriptors must be "small", positive integers. This means we can use a
|
||||
// VecMap which has that exact constraint for its keys. If that ever
|
||||
// changes, this will need to be changed to a different structure.
|
||||
tokens: VecMap<*mut Token>,
|
||||
pub(crate) timers: Rc<RefCell<TimerWheel>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Poll {
|
||||
#[cfg_attr(coverage, no_coverage)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Poll { ... }")
|
||||
}
|
||||
}
|
||||
|
||||
impl Poll {
|
||||
pub(crate) fn new(high_precision: bool) -> crate::Result<Poll> {
|
||||
Ok(Poll {
|
||||
poller: Poller::new(high_precision)?,
|
||||
tokens: VecMap::new(),
|
||||
timers: Rc::new(RefCell::new(TimerWheel::new())),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn poll(
|
||||
&mut self,
|
||||
mut timeout: Option<std::time::Duration>,
|
||||
) -> crate::Result<Vec<PollEvent>> {
|
||||
let now = std::time::Instant::now();
|
||||
// adjust the timeout for the timers
|
||||
if let Some(next_timeout) = self.timers.borrow().next_deadline() {
|
||||
if next_timeout <= now {
|
||||
timeout = Some(Duration::ZERO);
|
||||
} else if let Some(deadline) = timeout {
|
||||
timeout = Some(std::cmp::min(deadline, next_timeout - now));
|
||||
} else {
|
||||
timeout = Some(next_timeout - now);
|
||||
}
|
||||
};
|
||||
|
||||
let mut events = self.poller.poll(timeout)?;
|
||||
|
||||
// Update 'now' as some time may have elapsed in poll()
|
||||
let now = std::time::Instant::now();
|
||||
let mut timers = self.timers.borrow_mut();
|
||||
while let Some((_, token)) = timers.next_expired(now) {
|
||||
events.push(PollEvent {
|
||||
readiness: Readiness {
|
||||
readable: true,
|
||||
writable: false,
|
||||
error: false,
|
||||
},
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
/// Register a new file descriptor for polling
|
||||
///
|
||||
/// The file descriptor will be registered with given interest,
|
||||
/// mode and token. This function will fail if given a
|
||||
/// bad file descriptor or if the provided file descriptor is already
|
||||
/// registered.
|
||||
///
|
||||
/// # Leaking tokens
|
||||
///
|
||||
/// If your event source is dropped without being unregistered, the token
|
||||
/// passed in here will remain on the heap and continue to be used by the
|
||||
/// polling system even though no event source will match it.
|
||||
pub fn register(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: Token,
|
||||
) -> crate::Result<()> {
|
||||
let token_box = Box::new(token);
|
||||
let token_ptr = Box::into_raw(token_box);
|
||||
|
||||
let registration_result = self.poller.register(fd, interest, mode, token_ptr);
|
||||
|
||||
if registration_result.is_err() {
|
||||
// If registration did not work, do not add the file descriptor to
|
||||
// the token map. Instead, reconstruct the Box and drop it. This is
|
||||
// safe because it's from Box::into_raw() above.
|
||||
let token_box = unsafe { Box::from_raw(token_ptr) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// Registration worked, keep the token pointer until it's replaced
|
||||
// or removed.
|
||||
let index = index_from_fd(fd);
|
||||
if self.tokens.insert(index, token_ptr).is_some() {
|
||||
// If there is already a file descriptor associated with a
|
||||
// token, then replacing that entry will leak the token, but
|
||||
// converting it back into a Box might leave a dangling pointer
|
||||
// somewhere. We can theoretically continue safely by choosing
|
||||
// to leak, but one of our assumptions is no longer valid, so
|
||||
// panic.
|
||||
panic!("File descriptor ({}) already registered", fd);
|
||||
}
|
||||
}
|
||||
|
||||
registration_result
|
||||
}
|
||||
|
||||
/// Update the registration for a file descriptor
|
||||
///
|
||||
/// This allows you to change the interest, mode or token of a file
|
||||
/// descriptor. Fails if the provided fd is not currently registered.
|
||||
///
|
||||
/// See note on [`register()`](Self::register()) regarding leaking.
|
||||
pub fn reregister(
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
interest: Interest,
|
||||
mode: Mode,
|
||||
token: Token,
|
||||
) -> crate::Result<()> {
|
||||
let token_box = Box::new(token);
|
||||
let token_ptr = Box::into_raw(token_box);
|
||||
|
||||
let reregistration_result = self.poller.reregister(fd, interest, mode, token_ptr);
|
||||
|
||||
if reregistration_result.is_err() {
|
||||
// If registration did not work, do not add the file descriptor to
|
||||
// the token map. Instead, reconstruct the Box and drop it. This is
|
||||
// safe because it's from Box::into_raw() above.
|
||||
let token_box = unsafe { Box::from_raw(token_ptr) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// Registration worked, drop the old token memory and keep the new
|
||||
// token pointer until it's replaced or removed.
|
||||
let index = index_from_fd(fd);
|
||||
if let Some(previous) = self.tokens.insert(index, token_ptr) {
|
||||
// This is safe because it's from Box::into_raw() from a
|
||||
// previous (re-)register() call.
|
||||
let token_box = unsafe { Box::from_raw(previous) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// If there is no previous token registered for this file
|
||||
// descriptor, either the event source has wrongly called
|
||||
// reregister() without first being registered, or the
|
||||
// underlying poller has a dangling pointer. In the first case,
|
||||
// the reregistration should have failed; in the second case, we
|
||||
// cannot safely proceed.
|
||||
panic!("File descriptor ({}) had no previous registration", fd);
|
||||
}
|
||||
}
|
||||
|
||||
reregistration_result
|
||||
}
|
||||
|
||||
/// Unregister a file descriptor
|
||||
///
|
||||
/// This file descriptor will no longer generate events. Fails if the
|
||||
/// provided file descriptor is not currently registered.
|
||||
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
|
||||
let unregistration_result = self.poller.unregister(fd);
|
||||
|
||||
if unregistration_result.is_ok() {
|
||||
// The source was unregistered, we can remove the old token data.
|
||||
let index = index_from_fd(fd);
|
||||
if let Some(previous) = self.tokens.remove(index) {
|
||||
// This is safe because it's from Box::into_raw() from a
|
||||
// previous (re-)register() call.
|
||||
let token_box = unsafe { Box::from_raw(previous) };
|
||||
std::mem::drop(token_box);
|
||||
} else {
|
||||
// If there is no previous token registered for this file
|
||||
// descriptor, either the event source has wrongly called
|
||||
// unregister() without first being registered, or the
|
||||
// underlying poller has a dangling pointer. In the first case,
|
||||
// the reregistration should have failed; in the second case, we
|
||||
// cannot safely proceed.
|
||||
panic!("File descriptor ({}) had no previous registration", fd);
|
||||
}
|
||||
}
|
||||
|
||||
unregistration_result
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a file descriptor into an index for the token map. Panics if the
|
||||
/// file descriptor is negative.
|
||||
fn index_from_fd(fd: RawFd) -> usize {
|
||||
fd.try_into()
|
||||
.unwrap_or_else(|_| panic!("File descriptor ({}) is invalid", fd))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue