Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

91
third-party/vendor/calloop/src/error.rs vendored Normal file
View file

@ -0,0 +1,91 @@
//! Error types used and generated by Calloop.
//!
//! This module contains error types for Calloop's operations. They are designed
//! to make it easy to deal with errors arising from Calloop's internal I/O and
//! other operations.
//!
//! There are two top-level error types:
//!
//! - [`Error`]: used by callback functions, internal operations, and some event
//! loop API calls
//!
//! - [`InsertError`]: used primarily by the [`insert_source()`] method when an
//! event source cannot be added to the loop and needs to be given back to the
//! caller
//!
//! [`insert_source()`]: crate::LoopHandle::insert_source()
use std::fmt::{self, Debug, Formatter};
/// The primary error type used by Calloop covering internal errors and I/O
/// errors that arise during loop operations such as source registration or
/// event dispatching.
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// When an event source is registered (or re- or un-registered) with the
/// event loop, this error variant will occur if the token Calloop uses to
/// keep track of the event source is not valid.
#[error("invalid token provided to internal function")]
InvalidToken,
/// This variant wraps a [`std::io::Error`], which might arise from
/// Calloop's internal operations.
#[error("underlying IO error")]
IoError(#[from] std::io::Error),
/// Any other unexpected error kind (most likely from a user implementation of
/// [`EventSource::process_events()`]) will be wrapped in this.
///
/// [`EventSource::process_events()`]: crate::EventSource::process_events()
#[error("other error during loop operation")]
OtherError(#[from] Box<dyn std::error::Error + Sync + Send>),
}
impl From<nix::errno::Errno> for Error {
/// Converts a [`nix::Error`] into a wrapped version of the equivalent
/// [`std::io::Error`].
fn from(err: nix::errno::Errno) -> Self {
Into::<std::io::Error>::into(err).into()
}
}
impl From<Error> for std::io::Error {
/// Converts Calloop's error type into a [`std::io::Error`].
fn from(err: Error) -> Self {
match err {
Error::IoError(source) => source,
Error::InvalidToken => Self::new(std::io::ErrorKind::InvalidInput, err.to_string()),
Error::OtherError(source) => Self::new(std::io::ErrorKind::Other, source),
}
}
}
/// [`Result`] alias using Calloop's error type.
pub type Result<T> = core::result::Result<T, Error>;
/// An error generated when trying to insert an event source
#[derive(thiserror::Error)]
#[error("error inserting event source")]
pub struct InsertError<T> {
/// The source that could not be inserted
pub inserted: T,
/// The generated error
#[source]
pub error: Error,
}
impl<T> Debug for InsertError<T> {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, formatter: &mut Formatter) -> core::result::Result<(), fmt::Error> {
write!(formatter, "{:?}", self.error)
}
}
impl<T> From<InsertError<T>> for crate::Error {
/// Converts the [`InsertError`] into Calloop's error type, throwing away
/// the contained source.
#[cfg_attr(coverage, no_coverage)]
fn from(e: InsertError<T>) -> crate::Error {
e.error
}
}

545
third-party/vendor/calloop/src/io.rs vendored Normal file
View file

@ -0,0 +1,545 @@
//! Adapters for async IO objects
//!
//! This module mainly hosts the [`Async`] adapter for making IO objects async with readiness
//! monitoring backed by an [`EventLoop`](crate::EventLoop). See [`LoopHandle::adapt_io`] for
//! how to create them.
//!
//! [`LoopHandle::adapt_io`]: crate::LoopHandle#method.adapt_io
use std::cell::RefCell;
use std::os::unix::io::{AsRawFd, RawFd};
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll as TaskPoll, Waker};
use nix::fcntl::{fcntl, FcntlArg, OFlag};
#[cfg(feature = "futures-io")]
use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut};
use crate::{
loop_logic::LoopInner, sources::EventDispatcher, Interest, Mode, Poll, PostAction, Readiness,
Token, TokenFactory,
};
/// Adapter for async IO manipulations
///
/// This type wraps an IO object, providing methods to create futures waiting for its
/// readiness.
///
/// If the `futures-io` cargo feature is enabled, it also implements `AsyncRead` and/or
/// `AsyncWrite` if the underlying type implements `Read` and/or `Write`.
///
/// Note that this adapter and the futures procuded from it and *not* threadsafe.
pub struct Async<'l, F: AsRawFd> {
fd: Option<F>,
dispatcher: Rc<RefCell<IoDispatcher>>,
inner: Rc<dyn IoLoopInner + 'l>,
old_flags: OFlag,
}
impl<'l, F: AsRawFd + std::fmt::Debug> std::fmt::Debug for Async<'l, F> {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Async").field("fd", &self.fd).finish()
}
}
impl<'l, F: AsRawFd> Async<'l, F> {
pub(crate) fn new<Data>(inner: Rc<LoopInner<'l, Data>>, fd: F) -> crate::Result<Async<'l, F>> {
let rawfd = fd.as_raw_fd();
// set non-blocking
let old_flags = fcntl(rawfd, FcntlArg::F_GETFL)?;
let old_flags = unsafe { OFlag::from_bits_unchecked(old_flags) };
fcntl(rawfd, FcntlArg::F_SETFL(old_flags | OFlag::O_NONBLOCK))?;
// register in the loop
let dispatcher = Rc::new(RefCell::new(IoDispatcher {
fd: rawfd,
token: None,
waker: None,
is_registered: false,
interest: Interest::EMPTY,
last_readiness: Readiness::EMPTY,
}));
let key = inner.sources.borrow_mut().insert(dispatcher.clone());
dispatcher.borrow_mut().token = Some(Token { key, sub_id: 0 });
inner.register(&dispatcher)?;
// Straightforward casting would require us to add the bound `Data: 'l` but we don't actually need it
// as this module never accesses the dispatch data, so we use transmute to erase it
let inner: Rc<dyn IoLoopInner + 'l> =
unsafe { std::mem::transmute(inner as Rc<dyn IoLoopInner>) };
Ok(Async {
fd: Some(fd),
dispatcher,
inner,
old_flags,
})
}
/// Mutably access the underlying IO object
pub fn get_mut(&mut self) -> &mut F {
self.fd.as_mut().unwrap()
}
/// A future that resolves once the object becomes ready for reading
pub fn readable<'s>(&'s mut self) -> Readable<'s, 'l, F> {
Readable { io: self }
}
/// A future that resolves once the object becomes ready for writing
pub fn writable<'s>(&'s mut self) -> Writable<'s, 'l, F> {
Writable { io: self }
}
/// Remove the async adapter and retrieve the underlying object
pub fn into_inner(mut self) -> F {
self.fd.take().unwrap()
}
fn readiness(&self) -> Readiness {
self.dispatcher.borrow_mut().readiness()
}
fn register_waker(&self, interest: Interest, waker: Waker) -> crate::Result<()> {
{
let mut disp = self.dispatcher.borrow_mut();
disp.interest = interest;
disp.waker = Some(waker);
}
self.inner.reregister(&self.dispatcher)
}
}
/// A future that resolves once the associated object becomes ready for reading
#[derive(Debug)]
pub struct Readable<'s, 'l, F: AsRawFd> {
io: &'s mut Async<'l, F>,
}
impl<'s, 'l, F: AsRawFd> std::future::Future for Readable<'s, 'l, F> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> {
let io = &mut self.as_mut().io;
let readiness = io.readiness();
if readiness.readable || readiness.error {
TaskPoll::Ready(())
} else {
let _ = io.register_waker(Interest::READ, cx.waker().clone());
TaskPoll::Pending
}
}
}
/// A future that resolves once the associated object becomes ready for writing
#[derive(Debug)]
pub struct Writable<'s, 'l, F: AsRawFd> {
io: &'s mut Async<'l, F>,
}
impl<'s, 'l, F: AsRawFd> std::future::Future for Writable<'s, 'l, F> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<()> {
let io = &mut self.as_mut().io;
let readiness = io.readiness();
if readiness.writable || readiness.error {
TaskPoll::Ready(())
} else {
let _ = io.register_waker(Interest::WRITE, cx.waker().clone());
TaskPoll::Pending
}
}
}
impl<'l, F: AsRawFd> Drop for Async<'l, F> {
fn drop(&mut self) {
self.inner.kill(&self.dispatcher);
// restore flags
let _ = fcntl(
self.dispatcher.borrow().fd,
FcntlArg::F_SETFL(self.old_flags),
);
}
}
impl<'l, F: AsRawFd> Unpin for Async<'l, F> {}
trait IoLoopInner {
fn register(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()>;
fn reregister(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()>;
fn kill(&self, dispatcher: &RefCell<IoDispatcher>);
}
impl<'l, Data> IoLoopInner for LoopInner<'l, Data> {
fn register(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()> {
let disp = dispatcher.borrow();
self.poll.borrow_mut().register(
disp.fd,
Interest::EMPTY,
Mode::OneShot,
disp.token.expect("No token for IO dispatcher"),
)
}
fn reregister(&self, dispatcher: &RefCell<IoDispatcher>) -> crate::Result<()> {
let disp = dispatcher.borrow();
self.poll.borrow_mut().reregister(
disp.fd,
disp.interest,
Mode::OneShot,
disp.token.expect("No token for IO dispatcher"),
)
}
fn kill(&self, dispatcher: &RefCell<IoDispatcher>) {
let key = dispatcher
.borrow()
.token
.expect("No token for IO dispatcher")
.key;
let _source = self
.sources
.borrow_mut()
.remove(key)
.expect("Attempting to remove a non-existent source?!");
}
}
struct IoDispatcher {
fd: RawFd,
token: Option<Token>,
waker: Option<Waker>,
is_registered: bool,
interest: Interest,
last_readiness: Readiness,
}
impl IoDispatcher {
fn readiness(&mut self) -> Readiness {
std::mem::replace(&mut self.last_readiness, Readiness::EMPTY)
}
}
impl<Data> EventDispatcher<Data> for RefCell<IoDispatcher> {
fn process_events(
&self,
readiness: Readiness,
_token: Token,
_data: &mut Data,
) -> crate::Result<PostAction> {
let mut disp = self.borrow_mut();
disp.last_readiness = readiness;
if let Some(waker) = disp.waker.take() {
waker.wake();
}
Ok(PostAction::Continue)
}
fn register(&self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
// registration is handled by IoLoopInner
unreachable!()
}
fn reregister(&self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<bool> {
// registration is handled by IoLoopInner
unreachable!()
}
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool> {
let disp = self.borrow();
if disp.is_registered {
poll.unregister(disp.fd)?;
}
Ok(true)
}
fn pre_run(&self, _data: &mut Data) -> crate::Result<()> {
Ok(())
}
fn post_run(&self, _data: &mut Data) -> crate::Result<()> {
Ok(())
}
}
/*
* Async IO trait implementations
*/
#[cfg(feature = "futures-io")]
#[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))]
impl<'l, F: AsRawFd + std::io::Read> AsyncRead for Async<'l, F> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> TaskPoll<std::io::Result<usize>> {
match (*self).get_mut().read(buf) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
res => return TaskPoll::Ready(res),
}
self.register_waker(Interest::READ, cx.waker().clone())?;
TaskPoll::Pending
}
fn poll_read_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> TaskPoll<std::io::Result<usize>> {
match (*self).get_mut().read_vectored(bufs) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
res => return TaskPoll::Ready(res),
}
self.register_waker(Interest::READ, cx.waker().clone())?;
TaskPoll::Pending
}
}
#[cfg(feature = "futures-io")]
#[cfg_attr(docsrs, doc(cfg(feature = "futures-io")))]
impl<'l, F: AsRawFd + std::io::Write> AsyncWrite for Async<'l, F> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> TaskPoll<std::io::Result<usize>> {
match (*self).get_mut().write(buf) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
res => return TaskPoll::Ready(res),
}
self.register_waker(Interest::WRITE, cx.waker().clone())?;
TaskPoll::Pending
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> TaskPoll<std::io::Result<usize>> {
match (*self).get_mut().write_vectored(bufs) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
res => return TaskPoll::Ready(res),
}
self.register_waker(Interest::WRITE, cx.waker().clone())?;
TaskPoll::Pending
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<std::io::Result<()>> {
match (*self).get_mut().flush() {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {}
res => return TaskPoll::Ready(res),
}
self.register_waker(Interest::WRITE, cx.waker().clone())?;
TaskPoll::Pending
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> TaskPoll<std::io::Result<()>> {
self.poll_flush(cx)
}
}
#[cfg(all(test, feature = "executor", feature = "futures-io"))]
mod tests {
use futures::io::{AsyncReadExt, AsyncWriteExt};
use crate::sources::futures::executor;
#[test]
fn read_write() {
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (exec, sched) = executor().unwrap();
handle
.insert_source(exec, move |ret, &mut (), got| {
*got = ret;
})
.unwrap();
let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap();
let mut tx = handle.adapt_io(tx).unwrap();
let mut rx = handle.adapt_io(rx).unwrap();
let received = std::rc::Rc::new(std::cell::Cell::new(false));
let fut_received = received.clone();
sched
.schedule(async move {
let mut buf = [0; 12];
rx.read_exact(&mut buf).await.unwrap();
assert_eq!(&buf, b"Hello World!");
fut_received.set(true);
})
.unwrap();
// The receiving future alone cannot advance
event_loop
.dispatch(Some(std::time::Duration::from_millis(10)), &mut ())
.unwrap();
assert!(!received.get());
// schedule the writing future as well and wait until finish
sched
.schedule(async move {
tx.write_all(b"Hello World!").await.unwrap();
tx.flush().await.unwrap();
})
.unwrap();
while !received.get() {
event_loop.dispatch(None, &mut ()).unwrap();
}
}
#[test]
fn read_write_vectored() {
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (exec, sched) = executor().unwrap();
handle
.insert_source(exec, move |ret, &mut (), got| {
*got = ret;
})
.unwrap();
let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap();
let mut tx = handle.adapt_io(tx).unwrap();
let mut rx = handle.adapt_io(rx).unwrap();
let received = std::rc::Rc::new(std::cell::Cell::new(false));
let fut_received = received.clone();
sched
.schedule(async move {
let mut buf = [0; 12];
let mut ioslices = buf
.chunks_mut(2)
.map(std::io::IoSliceMut::new)
.collect::<Vec<_>>();
let count = rx.read_vectored(&mut ioslices).await.unwrap();
assert_eq!(count, 12);
assert_eq!(&buf, b"Hello World!");
fut_received.set(true);
})
.unwrap();
// The receiving future alone cannot advance
event_loop
.dispatch(Some(std::time::Duration::from_millis(10)), &mut ())
.unwrap();
assert!(!received.get());
// schedule the writing future as well and wait until finish
sched
.schedule(async move {
let buf = b"Hello World!";
let ioslices = buf.chunks(2).map(std::io::IoSlice::new).collect::<Vec<_>>();
let count = tx.write_vectored(&ioslices).await.unwrap();
assert_eq!(count, 12);
tx.flush().await.unwrap();
})
.unwrap();
while !received.get() {
event_loop.dispatch(None, &mut ()).unwrap();
}
}
#[test]
fn readable() {
use std::io::Write;
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (exec, sched) = executor().unwrap();
handle
.insert_source(exec, move |(), &mut (), got| {
*got = true;
})
.unwrap();
let (mut tx, rx) = std::os::unix::net::UnixStream::pair().unwrap();
let mut rx = handle.adapt_io(rx).unwrap();
sched
.schedule(async move {
rx.readable().await;
})
.unwrap();
let mut dispatched = false;
event_loop
.dispatch(Some(std::time::Duration::from_millis(100)), &mut dispatched)
.unwrap();
// The socket is not yet readable, so the readable() future has not completed
assert!(!dispatched);
tx.write_all(&[42]).unwrap();
tx.flush().unwrap();
// Now we should become readable
while !dispatched {
event_loop.dispatch(None, &mut dispatched).unwrap();
}
}
#[test]
fn writable() {
use std::io::{BufReader, BufWriter, Read, Write};
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (exec, sched) = executor().unwrap();
handle
.insert_source(exec, move |(), &mut (), got| {
*got = true;
})
.unwrap();
let (mut tx, mut rx) = std::os::unix::net::UnixStream::pair().unwrap();
tx.set_nonblocking(true).unwrap();
rx.set_nonblocking(true).unwrap();
// First, fill the socket buffers
{
let mut writer = BufWriter::new(&mut tx);
let data = vec![42u8; 1024];
loop {
if writer.write(&data).is_err() {
break;
}
}
}
// Now, wait for it to be readable
let mut tx = handle.adapt_io(tx).unwrap();
sched
.schedule(async move {
tx.writable().await;
})
.unwrap();
let mut dispatched = false;
event_loop
.dispatch(Some(std::time::Duration::from_millis(100)), &mut dispatched)
.unwrap();
// The socket is not yet writable, so the readable() future has not completed
assert!(!dispatched);
// now read everything
{
let mut reader = BufReader::new(&mut rx);
let mut buffer = vec![0u8; 1024];
loop {
if reader.read(&mut buffer).is_err() {
break;
}
}
}
// Now we should become writable
while !dispatched {
event_loop.dispatch(None, &mut dispatched).unwrap();
}
}
}

161
third-party/vendor/calloop/src/lib.rs vendored Normal file
View file

@ -0,0 +1,161 @@
//! Calloop, a Callback-based Event Loop
//!
//! This crate provides an [`EventLoop`] type, which is a small abstraction
//! over a polling system. The main difference between this crate
//! and other traditional rust event loops is that it is based on callbacks:
//! you can register several event sources, each being associated with a callback
//! closure that will be invoked whenever the associated event source generates
//! events.
//!
//! The main target use of this event loop is thus for apps that expect to spend
//! most of their time waiting for events and wishes to do so in a cheap and convenient
//! way. It is not meant for large scale high performance IO.
//!
//! ## How to use it
//!
//! Below is a quick usage example of calloop. For a more in-depth tutorial, see
//! the [calloop book](https://smithay.github.io/calloop).
//!
//! For simple uses, you can just add event sources with callbacks to the event
//! loop. For example, here's a runnable program that exits after five seconds:
//!
//! ```no_run
//! use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal};
//!
//! fn main() {
//! // Create the event loop. The loop is parameterised by the kind of shared
//! // data you want the callbacks to use. In this case, we want to be able to
//! // stop the loop when the timer fires, so we provide the loop with a
//! // LoopSignal, which has the ability to stop the loop from within events. We
//! // just annotate the type here; the actual data is provided later in the
//! // run() call.
//! let mut event_loop: EventLoop<LoopSignal> =
//! EventLoop::try_new().expect("Failed to initialize the event loop!");
//!
//! // Retrieve a handle. It is used to insert new sources into the event loop
//! // It can be cloned, allowing you to insert sources from within source
//! // callbacks.
//! let handle = event_loop.handle();
//!
//! // Create our event source, a timer, that will expire in 2 seconds
//! let source = Timer::from_duration(std::time::Duration::from_secs(2));
//!
//! // Inserting an event source takes this general form. It can also be done
//! // from within the callback of another event source.
//! handle
//! .insert_source(
//! // a type which implements the EventSource trait
//! source,
//! // a callback that is invoked whenever this source generates an event
//! |event, _metadata, shared_data| {
//! // This callback is given 3 values:
//! // - the event generated by the source (in our case, timer events are the Instant
//! // representing the deadline for which it has fired)
//! // - &mut access to some metadata, specific to the event source (in our case, a
//! // timer handle)
//! // - &mut access to the global shared data that was passed to EventLoop::run or
//! // EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
//! //
//! // The return type is just () because nothing uses it. Some
//! // sources will expect a Result of some kind instead.
//! println!("Timeout for {:?} expired!", event);
//! // notify the event loop to stop running using the signal in the shared data
//! // (see below)
//! shared_data.stop();
//! // The timer event source requires us to return a TimeoutAction to
//! // specify if the timer should be rescheduled. In our case we just drop it.
//! TimeoutAction::Drop
//! },
//! )
//! .expect("Failed to insert event source!");
//!
//! // Create the shared data for our loop.
//! let mut shared_data = event_loop.get_signal();
//!
//! // Actually run the event loop. This will dispatch received events to their
//! // callbacks, waiting at most 20ms for new events between each invocation of
//! // the provided callback (pass None for the timeout argument if you want to
//! // wait indefinitely between events).
//! //
//! // This is where we pass the *value* of the shared data, as a mutable
//! // reference that will be forwarded to all your callbacks, allowing them to
//! // share some state
//! event_loop
//! .run(
//! std::time::Duration::from_millis(20),
//! &mut shared_data,
//! |_shared_data| {
//! // Finally, this is where you can insert the processing you need
//! // to do do between each waiting event eg. drawing logic if
//! // you're doing a GUI app.
//! },
//! )
//! .expect("Error during event loop!");
//! }
//! ```
//!
//! ## Event source types
//!
//! The event loop is backed by an OS provided polling selector (epoll on Linux).
//!
//! This crate also provide some adapters for common event sources such as:
//!
//! - [MPSC channels](channel)
//! - [Timers](timer)
//! - [unix signals](signals) on Linux
//!
//! As well as generic objects backed by file descriptors.
//!
//! It is also possible to insert "idle" callbacks. These callbacks represent computations that
//! need to be done at some point, but are not as urgent as processing the events. These callbacks
//! are stored and then executed during [`EventLoop::dispatch`](EventLoop#method.dispatch), once all
//! events from the sources have been processed.
//!
//! ## Async/Await compatibility
//!
//! `calloop` can be used with futures, both as an executor and for monitoring Async IO.
//!
//! Activating the `executor` cargo feature will add the [`futures`] module, which provides
//! a future executor that can be inserted into an [`EventLoop`] as yet another [`EventSource`].
//!
//! IO objects can be made Async-aware via the [`LoopHandle::adapt_io`](LoopHandle#method.adapt_io)
//! method. Waking up the futures using these objects is handled by the associated [`EventLoop`]
//! directly.
//!
//! ## Custom event sources
//!
//! You can create custom event sources can will be inserted in the event loop by
//! implementing the [`EventSource`] trait. This can be done either directly from the file
//! descriptors of your source of interest, or by wrapping an other event source and further
//! processing its events. An [`EventSource`] can register more than one file descriptor and
//! aggregate them.
//!
//! ## Platforms support
//!
//! Currently, calloop is tested on Linux, FreeBSD and macOS.
//!
//! The following platforms are also enabled at compile time but not tested: Android, NetBSD,
//! OpenBSD, DragonFlyBSD.
//!
//! Those platforms *should* work based on the fact that they have the same polling mechanism as
//! tested platforms, but some subtle bugs might still occur.
#![warn(missing_docs, missing_debug_implementations)]
#![allow(clippy::needless_doctest_main)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(coverage, feature(no_coverage))]
mod sys;
pub use sys::{Interest, Mode, Poll, Readiness, Token, TokenFactory};
pub use self::loop_logic::{EventLoop, LoopHandle, LoopSignal, RegistrationToken};
pub use self::sources::*;
pub mod error;
pub use error::{Error, InsertError, Result};
pub mod io;
mod loop_logic;
mod macros;
mod sources;

View file

@ -0,0 +1,913 @@
use std::cell::{Cell, RefCell};
use std::fmt::Debug;
use std::io;
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use slotmap::SlotMap;
use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher};
use crate::{EventDispatcher, InsertError, Poll, PostAction, TokenFactory};
type IdleCallback<'i, Data> = Rc<RefCell<dyn IdleDispatcher<Data> + 'i>>;
slotmap::new_key_type! {
pub(crate) struct CalloopKey;
}
/// A token representing a registration in the [`EventLoop`].
///
/// This token is given to you by the [`EventLoop`] when an [`EventSource`] is inserted or
/// a [`Dispatcher`] is registered. You can use it to [disable](LoopHandle#method.disable),
/// [enable](LoopHandle#method.enable), [update`](LoopHandle#method.update),
/// [remove](LoopHandle#method.remove) or [kill](LoopHandle#method.kill) it.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct RegistrationToken {
key: CalloopKey,
}
pub(crate) struct LoopInner<'l, Data> {
pub(crate) poll: RefCell<Poll>,
pub(crate) sources: RefCell<SlotMap<CalloopKey, Rc<dyn EventDispatcher<Data> + 'l>>>,
idles: RefCell<Vec<IdleCallback<'l, Data>>>,
pending_action: Cell<PostAction>,
}
/// An handle to an event loop
///
/// This handle allows you to insert new sources and idles in this event loop,
/// it can be cloned, and it is possible to insert new sources from within a source
/// callback.
pub struct LoopHandle<'l, Data> {
inner: Rc<LoopInner<'l, Data>>,
}
impl<'l, Data> std::fmt::Debug for LoopHandle<'l, Data> {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("LoopHandle { ... }")
}
}
impl<'l, Data> Clone for LoopHandle<'l, Data> {
#[cfg_attr(coverage, no_coverage)]
fn clone(&self) -> Self {
LoopHandle {
inner: self.inner.clone(),
}
}
}
impl<'l, Data> LoopHandle<'l, Data> {
/// Inserts a new event source in the loop.
///
/// The provided callback will be called during the dispatching cycles whenever the
/// associated source generates events, see `EventLoop::dispatch(..)` for details.
///
/// This function takes ownership of the event source. Use `register_dispatcher`
/// if you need access to the event source after this call.
pub fn insert_source<S, F>(
&self,
source: S,
callback: F,
) -> Result<RegistrationToken, InsertError<S>>
where
S: EventSource + 'l,
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'l,
{
let dispatcher = Dispatcher::new(source, callback);
self.register_dispatcher(dispatcher.clone())
.map_err(|error| InsertError {
error,
inserted: dispatcher.into_source_inner(),
})
}
/// Registers a `Dispatcher` in the loop.
///
/// Use this function if you need access to the event source after its insertion in the loop.
///
/// See also `insert_source`.
pub fn register_dispatcher<S>(
&self,
dispatcher: Dispatcher<'l, S, Data>,
) -> crate::Result<RegistrationToken>
where
S: EventSource + 'l,
{
let mut sources = self.inner.sources.borrow_mut();
let mut poll = self.inner.poll.borrow_mut();
let key = sources.insert(dispatcher.clone_as_event_dispatcher());
let ret = sources
.get(key)
.unwrap()
.register(&mut *poll, &mut TokenFactory::new(key));
if let Err(error) = ret {
sources.remove(key).expect("Source was just inserted?!");
return Err(error);
}
Ok(RegistrationToken { key })
}
/// Inserts an idle callback.
///
/// This callback will be called during a dispatching cycle when the event loop has
/// finished processing all pending events from the sources and becomes idle.
pub fn insert_idle<'i, F: FnOnce(&mut Data) + 'l + 'i>(&self, callback: F) -> Idle<'i> {
let mut opt_cb = Some(callback);
let callback = Rc::new(RefCell::new(Some(move |data: &mut Data| {
if let Some(cb) = opt_cb.take() {
cb(data);
}
})));
self.inner.idles.borrow_mut().push(callback.clone());
Idle { callback }
}
/// Enables this previously disabled event source.
///
/// This previously disabled source will start generating events again.
///
/// **Note:** this cannot be done from within the source callback.
pub fn enable(&self, token: &RegistrationToken) -> crate::Result<()> {
if let Some(source) = self.inner.sources.borrow().get(token.key) {
source.register(
&mut *self.inner.poll.borrow_mut(),
&mut TokenFactory::new(token.key),
)?;
}
Ok(())
}
/// Makes this source update its registration.
///
/// If after accessing the source you changed its parameters in a way that requires
/// updating its registration.
pub fn update(&self, token: &RegistrationToken) -> crate::Result<()> {
if let Some(source) = self.inner.sources.borrow().get(token.key) {
if !source.reregister(
&mut *self.inner.poll.borrow_mut(),
&mut TokenFactory::new(token.key),
)? {
// we are in a callback, store for later processing
self.inner.pending_action.set(PostAction::Reregister);
}
}
Ok(())
}
/// Disables this event source.
///
/// The source remains in the event loop, but it'll no longer generate events
pub fn disable(&self, token: &RegistrationToken) -> crate::Result<()> {
if let Some(source) = self.inner.sources.borrow().get(token.key) {
if !source.unregister(&mut *self.inner.poll.borrow_mut())? {
// we are in a callback, store for later processing
self.inner.pending_action.set(PostAction::Disable);
}
}
Ok(())
}
/// Removes this source from the event loop.
pub fn remove(&self, token: RegistrationToken) {
if let Some(source) = self.inner.sources.borrow_mut().remove(token.key) {
if let Err(e) = source.unregister(&mut self.inner.poll.borrow_mut()) {
log::warn!(
"[calloop] Failed to unregister source from the polling system: {:?}",
e
);
}
}
}
/// Wrap an IO object into an async adapter
///
/// This adapter turns the IO object into an async-aware one that can be used in futures.
/// The readiness of these futures will be driven by the event loop.
///
/// The produced futures can be polled in any executor, and notably the one provided by
/// calloop.
pub fn adapt_io<F: AsRawFd>(&self, fd: F) -> crate::Result<crate::io::Async<'l, F>> {
crate::io::Async::new(self.inner.clone(), fd)
}
}
/// An event loop
///
/// This loop can host several event sources, that can be dynamically added or removed.
pub struct EventLoop<'l, Data> {
handle: LoopHandle<'l, Data>,
stop_signal: Arc<AtomicBool>,
ping: crate::sources::ping::Ping,
}
impl<'l, Data> std::fmt::Debug for EventLoop<'l, Data> {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("EventLoop { ... }")
}
}
impl<'l, Data> EventLoop<'l, Data> {
/// Create a new event loop
///
/// Fails if the initialization of the polling system failed.
pub fn try_new() -> crate::Result<Self> {
Self::inner_new(false)
}
/// Create a new event loop in high precision mode
///
/// On some platforms it requires to setup more resources to enable high-precision
/// (sub millisecond) capabilities, so you should use this constructor if you need
/// this kind of precision.
///
/// Fails if the initialization of the polling system failed.
pub fn try_new_high_precision() -> crate::Result<Self> {
Self::inner_new(true)
}
fn inner_new(high_precision: bool) -> crate::Result<Self> {
let poll = Poll::new(high_precision)?;
let handle = LoopHandle {
inner: Rc::new(LoopInner {
poll: RefCell::new(poll),
sources: RefCell::new(SlotMap::with_key()),
idles: RefCell::new(Vec::new()),
pending_action: Cell::new(PostAction::Continue),
}),
};
let (ping, ping_source) = crate::sources::ping::make_ping()?;
handle.insert_source(ping_source, |_, _, _| {})?;
Ok(EventLoop {
handle,
stop_signal: Arc::new(AtomicBool::new(false)),
ping,
})
}
/// Retrieve a loop handle
pub fn handle(&self) -> LoopHandle<'l, Data> {
self.handle.clone()
}
fn dispatch_events(
&mut self,
mut timeout: Option<Duration>,
data: &mut Data,
) -> crate::Result<()> {
let now = Instant::now();
let events = {
let mut poll = self.handle.inner.poll.borrow_mut();
loop {
let result = poll.poll(timeout);
match result {
Ok(events) => break events,
Err(crate::Error::IoError(err)) if err.kind() == io::ErrorKind::Interrupted => {
// Interrupted by a signal. Update timeout and retry.
if let Some(to) = timeout {
let elapsed = now.elapsed();
if elapsed >= to {
return Ok(());
} else {
timeout = Some(to - elapsed);
}
}
}
Err(err) => return Err(err),
};
}
};
for event in events {
let opt_disp = self
.handle
.inner
.sources
.borrow()
.get(event.token.key)
.cloned();
if let Some(disp) = opt_disp {
let mut ret = disp.process_events(event.readiness, event.token, data)?;
// if the returned PostAction is Continue, it may be overwritten by an user-specified pending action
let pending_action = self
.handle
.inner
.pending_action
.replace(PostAction::Continue);
if let PostAction::Continue = ret {
ret = pending_action;
}
match ret {
PostAction::Reregister => {
disp.reregister(
&mut self.handle.inner.poll.borrow_mut(),
&mut TokenFactory::new(event.token.key),
)?;
}
PostAction::Disable => {
disp.unregister(&mut self.handle.inner.poll.borrow_mut())?;
}
PostAction::Remove => {
// delete the source from the list, it'll be cleaned up with the if just below
self.handle
.inner
.sources
.borrow_mut()
.remove(event.token.key);
}
PostAction::Continue => {}
}
if !self
.handle
.inner
.sources
.borrow()
.contains_key(event.token.key)
{
// the source has been removed from within its callback, unregister it
let mut poll = self.handle.inner.poll.borrow_mut();
if let Err(e) = disp.unregister(&mut *poll) {
log::warn!(
"[calloop] Failed to unregister source from the polling system: {:?}",
e
);
}
}
} else {
log::warn!(
"[calloop] Received an event for non-existence source: {:?}",
event.token.key
);
}
}
Ok(())
}
fn dispatch_idles(&mut self, data: &mut Data) {
let idles = std::mem::take(&mut *self.handle.inner.idles.borrow_mut());
for idle in idles {
idle.borrow_mut().dispatch(data);
}
}
fn invoke_pre_run(&self, data: &mut Data) -> crate::Result<()> {
let sources = self
.handle
.inner
.sources
.borrow()
.values()
.cloned()
.collect::<Vec<_>>();
for source in sources {
source.pre_run(data)?;
}
Ok(())
}
fn invoke_post_run(&self, data: &mut Data) -> crate::Result<()> {
let sources = self
.handle
.inner
.sources
.borrow()
.values()
.cloned()
.collect::<Vec<_>>();
for source in sources {
source.post_run(data)?;
}
Ok(())
}
/// Dispatch pending events to their callbacks
///
/// If some sources have events available, their callbacks will be immediatly called.
/// Otherwise this will wait until an event is receive or the provided `timeout`
/// is reached. If `timeout` is `None`, it will wait without a duration limit.
///
/// Once pending events have been processed or the timeout is reached, all pending
/// idle callbacks will be fired before this method returns.
pub fn dispatch<D: Into<Option<Duration>>>(
&mut self,
timeout: D,
data: &mut Data,
) -> crate::Result<()> {
self.invoke_pre_run(data)?;
self.dispatch_events(timeout.into(), data)?;
self.dispatch_idles(data);
self.invoke_post_run(data)?;
Ok(())
}
/// Get a signal to stop this event loop from running
///
/// To be used in conjunction with the `run()` method.
pub fn get_signal(&self) -> LoopSignal {
LoopSignal {
signal: self.stop_signal.clone(),
ping: self.ping.clone(),
}
}
/// Run this event loop
///
/// This will repeatedly try to dispatch events (see the `dispatch()` method) on
/// this event loop, waiting at most `timeout` every time.
///
/// Between each dispatch wait, your provided callback will be called.
///
/// You can use the `get_signal()` method to retrieve a way to stop or wakeup
/// the event loop from anywhere.
pub fn run<F, D: Into<Option<Duration>>>(
&mut self,
timeout: D,
data: &mut Data,
mut cb: F,
) -> crate::Result<()>
where
F: FnMut(&mut Data),
{
let timeout = timeout.into();
self.stop_signal.store(false, Ordering::Release);
self.invoke_pre_run(data)?;
while !self.stop_signal.load(Ordering::Acquire) {
self.dispatch_events(timeout, data)?;
self.dispatch_idles(data);
cb(data);
}
self.invoke_post_run(data)?;
Ok(())
}
}
/// A signal that can be shared between thread to stop or wakeup a running
/// event loop
#[derive(Clone)]
pub struct LoopSignal {
signal: Arc<AtomicBool>,
ping: crate::sources::ping::Ping,
}
impl std::fmt::Debug for LoopSignal {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("LoopSignal { ... }")
}
}
impl LoopSignal {
/// Stop the event loop
///
/// Once this method is called, the next time the event loop has finished
/// waiting for events, it will return rather than starting to wait again.
///
/// This is only usefull if you are using the `EventLoop::run()` method.
pub fn stop(&self) {
self.signal.store(true, Ordering::Release);
}
/// Wake up the event loop
///
/// This sends a dummy event to the event loop to simulate the reception
/// of an event, making the wait return early. Called after `stop()`, this
/// ensures the event loop will terminate quickly if you specified a long
/// timeout (or no timeout at all) to the `dispatch` or `run` method.
pub fn wakeup(&self) {
self.ping.ping();
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::{
generic::Generic, ping::*, Dispatcher, Interest, Mode, Poll, PostAction, Readiness,
RegistrationToken, Token, TokenFactory,
};
use super::EventLoop;
#[test]
fn dispatch_idle() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = false;
event_loop.handle().insert_idle(|d| {
*d = true;
});
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
assert!(dispatched);
}
#[test]
fn cancel_idle() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = false;
let handle = event_loop.handle();
let idle = handle.insert_idle(move |d| {
*d = true;
});
idle.cancel();
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
}
#[test]
fn wakeup() {
let mut event_loop = EventLoop::try_new().unwrap();
let signal = event_loop.get_signal();
::std::thread::spawn(move || {
::std::thread::sleep(Duration::from_millis(500));
signal.wakeup();
});
// the test should return
event_loop.dispatch(None, &mut ()).unwrap();
}
#[test]
fn wakeup_stop() {
let mut event_loop = EventLoop::try_new().unwrap();
let signal = event_loop.get_signal();
::std::thread::spawn(move || {
::std::thread::sleep(Duration::from_millis(500));
signal.stop();
signal.wakeup();
});
// the test should return
event_loop.run(None, &mut (), |_| {}).unwrap();
}
#[test]
fn insert_bad_source() {
let event_loop = EventLoop::<()>::try_new().unwrap();
let ret = event_loop.handle().insert_source(
crate::sources::generic::Generic::new(420, Interest::READ, Mode::Level),
|_, _, _| Ok(PostAction::Continue),
);
assert!(ret.is_err());
}
#[test]
fn insert_source_no_interest() {
let event_loop = EventLoop::<()>::try_new().unwrap();
let ret = event_loop.handle().insert_source(
crate::sources::generic::Generic::new(0, Interest::EMPTY, Mode::Level),
|_, _, _| Ok(PostAction::Continue),
);
assert!(ret.is_ok());
}
#[test]
fn disarm_rearm() {
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
let (ping, ping_source) = make_ping().unwrap();
let ping_token = event_loop
.handle()
.insert_source(ping_source, |(), &mut (), dispatched| {
*dispatched = true;
})
.unwrap();
ping.ping();
let mut dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// disable the source
ping.ping();
event_loop.handle().disable(&ping_token).unwrap();
let mut dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
// disabling it again is an error
event_loop.handle().disable(&ping_token).unwrap_err();
// reenable it, the previous ping now gets dispatched
event_loop.handle().enable(&ping_token).unwrap();
let mut dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
}
#[test]
fn multiple_tokens() {
struct DoubleSource {
ping1: PingSource,
ping2: PingSource,
}
impl crate::EventSource for DoubleSource {
type Event = u32;
type Metadata = ();
type Ret = ();
type Error = PingError;
fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.ping1
.process_events(readiness, token, |(), &mut ()| callback(1, &mut ()))?;
self.ping2
.process_events(readiness, token, |(), &mut ()| callback(2, &mut ()))?;
Ok(PostAction::Continue)
}
fn register(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.ping1.register(poll, token_factory)?;
self.ping2.register(poll, token_factory)?;
Ok(())
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.ping1.reregister(poll, token_factory)?;
self.ping2.reregister(poll, token_factory)?;
Ok(())
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.ping1.unregister(poll)?;
self.ping2.unregister(poll)?;
Ok(())
}
}
let mut event_loop = EventLoop::<u32>::try_new().unwrap();
let (ping1, source1) = make_ping().unwrap();
let (ping2, source2) = make_ping().unwrap();
let source = DoubleSource {
ping1: source1,
ping2: source2,
};
event_loop
.handle()
.insert_source(source, |i, _, d| {
eprintln!("Dispatching {}", i);
*d += i
})
.unwrap();
let mut dispatched = 0;
ping1.ping();
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert_eq!(dispatched, 1);
dispatched = 0;
ping2.ping();
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert_eq!(dispatched, 2);
dispatched = 0;
ping1.ping();
ping2.ping();
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert_eq!(dispatched, 3);
}
#[test]
fn change_interests() {
use nix::sys::socket::{recv, socketpair, AddressFamily, MsgFlags, SockFlag, SockType};
use nix::unistd::write;
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
let (sock1, sock2) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(), // recv with DONTWAIT will suffice for platforms without SockFlag::SOCK_NONBLOCKING such as macOS
)
.unwrap();
let source = Generic::new(sock1, Interest::READ, Mode::Level);
let dispatcher = Dispatcher::new(source, |_, &mut fd, dispatched| {
*dispatched = true;
// read all contents available to drain the socket
let mut buf = [0u8; 32];
loop {
match recv(fd, &mut buf, MsgFlags::MSG_DONTWAIT) {
Ok(0) => break, // closed pipe, we are now inert
Ok(_) => {}
Err(e) => {
let e: std::io::Error = e.into();
if e.kind() == std::io::ErrorKind::WouldBlock {
break;
// nothing more to read
} else {
// propagate error
return Err(e);
}
}
}
}
Ok(PostAction::Continue)
});
let sock_token_1 = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
// first dispatch, nothing is readable
let mut dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
// write something, the socket becomes readable
write(sock2, &[1, 2, 3]).unwrap();
dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// All has been read, no longer readable
dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
// change the interests for writability instead
dispatcher.as_source_mut().interest = Interest::WRITE;
event_loop.handle().update(&sock_token_1).unwrap();
// the socket is writable
dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// change back to readable
dispatcher.as_source_mut().interest = Interest::READ;
event_loop.handle().update(&sock_token_1).unwrap();
// the socket is not readable
dispatched = false;
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
}
#[test]
fn kill_source() {
let mut event_loop = EventLoop::<Option<RegistrationToken>>::try_new().unwrap();
let handle = event_loop.handle();
let (ping, ping_source) = make_ping().unwrap();
let ping_token = event_loop
.handle()
.insert_source(ping_source, move |(), &mut (), opt_src| {
if let Some(src) = opt_src.take() {
handle.remove(src);
}
})
.unwrap();
ping.ping();
let mut opt_src = Some(ping_token);
event_loop.dispatch(Duration::ZERO, &mut opt_src).unwrap();
assert!(opt_src.is_none());
}
#[test]
fn non_static_data() {
use std::sync::mpsc;
let (sender, receiver) = mpsc::channel();
{
struct RefSender<'a>(&'a mpsc::Sender<()>);
let mut ref_sender = RefSender(&sender);
let mut event_loop = EventLoop::<RefSender<'_>>::try_new().unwrap();
let (ping, ping_source) = make_ping().unwrap();
let _ping_token = event_loop
.handle()
.insert_source(ping_source, |_, _, ref_sender| {
ref_sender.0.send(()).unwrap();
})
.unwrap();
ping.ping();
event_loop
.dispatch(Duration::ZERO, &mut ref_sender)
.unwrap();
}
receiver.recv().unwrap();
// sender still usable (e.g. for another EventLoop)
drop(sender);
}
// A dummy EventSource to test insertion and removal of sources
struct DummySource;
impl crate::EventSource for DummySource {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = crate::Error;
fn process_events<F>(
&mut self,
_: Readiness,
_: Token,
mut callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
callback((), &mut ());
Ok(PostAction::Continue)
}
fn register(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
Ok(())
}
fn reregister(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
Ok(())
}
fn unregister(&mut self, _: &mut Poll) -> crate::Result<()> {
Ok(())
}
}
}

223
third-party/vendor/calloop/src/macros.rs vendored Normal file
View file

@ -0,0 +1,223 @@
//! Macros for helping with common operations in Calloop.
/// Register a set of event sources. Effectively calls
/// [`EventSource::register()`] for all the sources provided.
///
/// Usage:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// calloop::batch_register!(
/// poll, token_factory,
/// self.source_one,
/// self.source_two,
/// self.source_three,
/// self.source_four,
/// )
/// ```
///
/// Note that there is no scope for customisation; if you need to do special
/// things with a particular source, you'll need to leave it off the list. Also
/// note that this only does try-or-early-return error handling in the order
/// that you list the sources; if you need anything else, don't use this macro.
///
/// [`EventSource::register()`]: crate::EventSource::register()
#[macro_export]
macro_rules! batch_register {
($poll:ident, $token_fac:ident, $( $source:expr ),* $(,)?) => {
{
$(
$source.register($poll, $token_fac)?;
)*
$crate::Result::<_>::Ok(())
}
};
}
/// Reregister a set of event sources. Effectively calls
/// [`EventSource::reregister()`] for all the sources provided.
///
/// Usage:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// calloop::batch_reregister!(
/// poll, token_factory,
/// self.source_one,
/// self.source_two,
/// self.source_three,
/// self.source_four,
/// )
/// ```
///
/// Note that there is no scope for customisation; if you need to do special
/// things with a particular source, you'll need to leave it off the list. Also
/// note that this only does try-or-early-return error handling in the order
/// that you list the sources; if you need anything else, don't use this macro.
///
/// [`EventSource::reregister()`]: crate::EventSource::reregister()
#[macro_export]
macro_rules! batch_reregister {
($poll:ident, $token_fac:ident, $( $source:expr ),* $(,)?) => {
{
$(
$source.reregister($poll, $token_fac)?;
)*
$crate::Result::<_>::Ok(())
}
};
}
/// Unregister a set of event sources. Effectively calls
/// [`EventSource::unregister()`] for all the sources provided.
///
/// Usage:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// calloop::batch_unregister!(
/// poll,
/// self.source_one,
/// self.source_two,
/// self.source_three,
/// self.source_four,
/// )
/// ```
///
/// Note that there is no scope for customisation; if you need to do special
/// things with a particular source, you'll need to leave it off the list. Also
/// note that this only does try-or-early-return error handling in the order
/// that you list the sources; if you need anything else, don't use this macro.
///
/// [`EventSource::unregister()`]: crate::EventSource::unregister()
#[macro_export]
macro_rules! batch_unregister {
($poll:ident, $( $source:expr ),* $(,)?) => {
{
$(
$source.unregister($poll)?;
)*
$crate::Result::<_>::Ok(())
}
};
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::{
ping::{make_ping, PingSource},
EventSource, PostAction,
};
struct BatchSource {
ping0: PingSource,
ping1: PingSource,
ping2: PingSource,
}
impl EventSource for BatchSource {
type Event = usize;
type Metadata = ();
type Ret = ();
type Error = Box<dyn std::error::Error + Sync + Send>;
fn process_events<F>(
&mut self,
readiness: crate::Readiness,
token: crate::Token,
mut callback: F,
) -> Result<crate::PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.ping0
.process_events(readiness, token, |_, m| callback(0, m))?;
self.ping1
.process_events(readiness, token, |_, m| callback(1, m))?;
self.ping2
.process_events(readiness, token, |_, m| callback(2, m))?;
Ok(PostAction::Continue)
}
fn register(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
crate::batch_register!(poll, token_factory, self.ping0, self.ping1, self.ping2)
}
fn reregister(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
crate::batch_reregister!(poll, token_factory, self.ping0, self.ping1, self.ping2)
}
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
crate::batch_unregister!(poll, self.ping0, self.ping1, self.ping2)
}
}
#[test]
fn test_batch_operations() {
let mut fired = [false; 3];
let (send0, ping0) = make_ping().unwrap();
let (send1, ping1) = make_ping().unwrap();
let (send2, ping2) = make_ping().unwrap();
let top = BatchSource {
ping0,
ping1,
ping2,
};
let mut event_loop = crate::EventLoop::<[bool; 3]>::try_new().unwrap();
let handle = event_loop.handle();
let token = handle
.insert_source(top, |idx, _, fired| {
fired[idx] = true;
})
.unwrap();
send0.ping();
send1.ping();
send2.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert_eq!(fired, [true; 3]);
fired = [false; 3];
handle.update(&token).unwrap();
send0.ping();
send1.ping();
send2.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert_eq!(fired, [true; 3]);
fired = [false; 3];
handle.remove(token);
send0.ping();
send1.ping();
send2.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert_eq!(fired, [false; 3]);
}
}

View file

@ -0,0 +1,327 @@
//! An MPSC channel whose receiving end is an event source
//!
//! Create a channel using [`channel()`](channel), which returns a
//! [`Sender`] that can be cloned and sent accross threads if `T: Send`,
//! and a [`Channel`] that can be inserted into an [`EventLoop`](crate::EventLoop).
//! It will generate one event per message.
//!
//! A synchronous version of the channel is provided by [`sync_channel`], in which
//! the [`SyncSender`] will block when the channel is full.
use std::sync::mpsc;
use crate::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
use super::ping::{make_ping, Ping, PingError, PingSource};
/// The events generated by the channel event source
#[derive(Debug)]
pub enum Event<T> {
/// A message was received and is bundled here
Msg(T),
/// The channel was closed
///
/// This means all the `Sender`s associated with this channel
/// have been dropped, no more messages will ever be received.
Closed,
}
/// The sender end of a channel
///
/// It can be cloned and sent accross threads (if `T` is).
#[derive(Debug)]
pub struct Sender<T> {
sender: mpsc::Sender<T>,
ping: Ping,
}
impl<T> Clone for Sender<T> {
#[cfg_attr(coverage, no_coverage)]
fn clone(&self) -> Sender<T> {
Sender {
sender: self.sender.clone(),
ping: self.ping.clone(),
}
}
}
impl<T> Sender<T> {
/// Send a message to the channel
///
/// This will wake the event loop and deliver an `Event::Msg` to
/// it containing the provided value.
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
self.sender.send(t).map(|()| self.ping.ping())
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
// ping on drop, to notify about channel closure
self.ping.ping();
}
}
/// The sender end of a synchronous channel
///
/// It can be cloned and sent accross threads (if `T` is).
#[derive(Debug)]
pub struct SyncSender<T> {
sender: mpsc::SyncSender<T>,
ping: Ping,
}
impl<T> Clone for SyncSender<T> {
#[cfg_attr(coverage, no_coverage)]
fn clone(&self) -> SyncSender<T> {
SyncSender {
sender: self.sender.clone(),
ping: self.ping.clone(),
}
}
}
impl<T> SyncSender<T> {
/// Send a message to the synchronous channel
///
/// This will wake the event loop and deliver an `Event::Msg` to
/// it containing the provided value. If the channel is full, this
/// function will block until the event loop empties it and it can
/// deliver the message.
///
/// Due to the blocking behavior, this method should not be used on the
/// same thread as the one running the event loop, as it could cause deadlocks.
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
let ret = self.try_send(t);
match ret {
Ok(()) => Ok(()),
Err(mpsc::TrySendError::Full(t)) => self.sender.send(t).map(|()| self.ping.ping()),
Err(mpsc::TrySendError::Disconnected(t)) => Err(mpsc::SendError(t)),
}
}
/// Send a message to the synchronous channel
///
/// This will wake the event loop and deliver an `Event::Msg` to
/// it containing the provided value. If the channel is full, this
/// function will return an error, but the event loop will still be
/// signaled for readiness.
pub fn try_send(&self, t: T) -> Result<(), mpsc::TrySendError<T>> {
let ret = self.sender.try_send(t);
if let Ok(()) | Err(mpsc::TrySendError::Full(_)) = ret {
self.ping.ping();
}
ret
}
}
/// The receiving end of the channel
///
/// This is the event source to be inserted into your `EventLoop`.
#[derive(Debug)]
pub struct Channel<T> {
receiver: mpsc::Receiver<T>,
source: PingSource,
}
// This impl is safe because the Channel is only able to move around threads
// when it is not inserted into an event loop. (Otherwise it is stuck into
// a Source<_> and the internals of calloop, which are not Send).
// At this point, the Arc<Receiver> has a count of 1, and it is obviously
// safe to Send between threads.
unsafe impl<T: Send> Send for Channel<T> {}
impl<T> Channel<T> {
/// Proxy for [`mpsc::Receiver::recv`] to manually poll events.
///
/// *Note*: Normally you would want to use the `Channel` by inserting
/// it into an event loop instead. Use this for example to immediately
/// dispatch pending events after creation.
pub fn recv(&self) -> Result<T, mpsc::RecvError> {
self.receiver.recv()
}
/// Proxy for [`mpsc::Receiver::try_recv`] to manually poll events.
///
/// *Note*: Normally you would want to use the `Channel` by inserting
/// it into an event loop instead. Use this for example to immediately
/// dispatch pending events after creation.
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
self.receiver.try_recv()
}
}
/// Create a new asynchronous channel
pub fn channel<T>() -> (Sender<T>, Channel<T>) {
let (sender, receiver) = mpsc::channel();
let (ping, source) = make_ping().expect("Failed to create a Ping.");
(Sender { sender, ping }, Channel { receiver, source })
}
/// Create a new synchronous, bounded channel
pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Channel<T>) {
let (sender, receiver) = mpsc::sync_channel(bound);
let (ping, source) = make_ping().expect("Failed to create a Ping.");
(SyncSender { sender, ping }, Channel { receiver, source })
}
impl<T> EventSource for Channel<T> {
type Event = Event<T>;
type Metadata = ();
type Ret = ();
type Error = ChannelError;
fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
let receiver = &self.receiver;
self.source
.process_events(readiness, token, |(), &mut ()| loop {
match receiver.try_recv() {
Ok(val) => callback(Event::Msg(val), &mut ()),
Err(mpsc::TryRecvError::Empty) => break,
Err(mpsc::TryRecvError::Disconnected) => {
callback(Event::Closed, &mut ());
break;
}
}
})
.map_err(ChannelError)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.source.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.source.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.source.unregister(poll)
}
}
/// An error arising from processing events for a channel.
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct ChannelError(PingError);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_channel() {
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (tx, rx) = channel::<()>();
// (got_msg, got_closed)
let mut got = (false, false);
let _channel_token = handle
.insert_source(rx, move |evt, &mut (), got: &mut (bool, bool)| match evt {
Event::Msg(()) => {
got.0 = true;
}
Event::Closed => {
got.1 = true;
}
})
.unwrap();
// nothing is sent, nothing is received
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
.unwrap();
assert_eq!(got, (false, false));
// a message is send
tx.send(()).unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
.unwrap();
assert_eq!(got, (true, false));
// the sender is dropped
::std::mem::drop(tx);
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
.unwrap();
assert_eq!(got, (true, true));
}
#[test]
fn basic_sync_channel() {
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (tx, rx) = sync_channel::<()>(2);
let mut received = (0, false);
let _channel_token = handle
.insert_source(
rx,
move |evt, &mut (), received: &mut (u32, bool)| match evt {
Event::Msg(()) => {
received.0 += 1;
}
Event::Closed => {
received.1 = true;
}
},
)
.unwrap();
// nothing is sent, nothing is received
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut received)
.unwrap();
assert_eq!(received.0, 0);
assert!(!received.1);
// fill the channel
tx.send(()).unwrap();
tx.send(()).unwrap();
assert!(tx.try_send(()).is_err());
// empty it
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut received)
.unwrap();
assert_eq!(received.0, 2);
assert!(!received.1);
// send a final message and drop the sender
tx.send(()).unwrap();
std::mem::drop(tx);
// final read of the channel
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut received)
.unwrap();
assert_eq!(received.0, 3);
assert!(received.1);
}
}

View file

@ -0,0 +1,212 @@
//! A futures executor as an event source
//!
//! Only available with the `executor` cargo feature of `calloop`.
//!
//! This executor is intended for light futures, which will be polled as part of your
//! event loop. Such futures may be waiting for IO, or for some external computation on an
//! other thread for example.
//!
//! You can create a new executor using the `executor` function, which creates a pair
//! `(Executor<T>, Scheduler<T>)` to handle futures that all evaluate to type `T`. The
//! executor should be inserted into your event loop, and will yield the return values of
//! the futures as they finish into your callback. The scheduler can be cloned and used
//! to send futures to be executed into the executor. A generic executor can be obtained
//! by choosing `T = ()` and letting futures handle the forwarding of their return values
//! (if any) by their own means.
//!
//! **Note:** The futures must have their own means of being woken up, as this executor is,
//! by itself, not I/O aware. See [`LoopHandle::adapt_io`](crate::LoopHandle#method.adapt_io)
//! for that, or you can use some other mechanism if you prefer.
use std::{future::Future, pin::Pin, sync::Arc};
use futures_util::{
stream::{FuturesUnordered, Stream},
task::{waker_ref, ArcWake, Context, LocalFutureObj, Poll as FutPoll},
};
use crate::{
sources::{
channel::{channel, Channel, ChannelError, Event, Sender},
ping::{make_ping, Ping, PingError, PingSource},
EventSource,
},
Poll, PostAction, Readiness, Token, TokenFactory,
};
/// A future executor as an event source
#[derive(Debug)]
pub struct Executor<T> {
futures: FuturesUnordered<LocalFutureObj<'static, T>>,
new_futures: Channel<LocalFutureObj<'static, T>>,
ready_futures: PingSource,
waker: Arc<ExecWaker>,
}
/// A scheduler to send futures to an executor
#[derive(Clone, Debug)]
pub struct Scheduler<T> {
sender: Sender<LocalFutureObj<'static, T>>,
}
impl<T> Scheduler<T> {
/// Sends the given future to the executor associated to this scheduler
///
/// Returns an error if the the executor not longer exists.
pub fn schedule<Fut: 'static>(&self, future: Fut) -> Result<(), ExecutorDestroyed>
where
Fut: Future<Output = T>,
{
let obj = LocalFutureObj::new(Box::new(future));
self.sender.send(obj).map_err(|_| ExecutorDestroyed)
}
}
/// Error generated when trying to schedule a future after the
/// executor was destroyed.
#[derive(thiserror::Error, Debug)]
#[error("the executor was destroyed")]
pub struct ExecutorDestroyed;
#[derive(Debug)]
struct ExecWaker {
ping: Ping,
}
impl ArcWake for ExecWaker {
fn wake_by_ref(arc_self: &Arc<ExecWaker>) {
arc_self.ping.ping();
}
}
/// Create a new executor, and its associated scheduler
///
/// May fail due to OS errors preventing calloop to setup its internal pipes (if your
/// process has reatched its file descriptor limit for example).
pub fn executor<T>() -> crate::Result<(Executor<T>, Scheduler<T>)> {
let (ping, ready_futures) = make_ping()?;
let (sender, new_futures) = channel();
Ok((
Executor {
futures: FuturesUnordered::new(),
new_futures,
ready_futures,
waker: Arc::new(ExecWaker { ping }),
},
Scheduler { sender },
))
}
impl<T> EventSource for Executor<T> {
type Event = T;
type Metadata = ();
type Ret = ();
type Error = ExecutorError;
fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(T, &mut ()),
{
// fetch all newly inserted futures and push them to the container
let futures = &mut self.futures;
self.new_futures
.process_events(readiness, token, |evt, _| {
if let Event::Msg(fut) = evt {
futures.push(fut);
}
})
.map_err(ExecutorError::NewFutureError)?;
// process ping events to make it non-ready again
self.ready_futures
.process_events(readiness, token, |(), _| {})
.map_err(ExecutorError::WakeError)?;
// advance all available futures as much as possible
let waker = waker_ref(&self.waker);
let mut cx = Context::from_waker(&waker);
while let FutPoll::Ready(Some(ret)) = Pin::new(&mut self.futures).poll_next(&mut cx) {
callback(ret, &mut ());
}
Ok(PostAction::Continue)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.new_futures.register(poll, token_factory)?;
self.ready_futures.register(poll, token_factory)?;
Ok(())
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.new_futures.reregister(poll, token_factory)?;
self.ready_futures.reregister(poll, token_factory)?;
Ok(())
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.new_futures.unregister(poll)?;
self.ready_futures.unregister(poll)?;
Ok(())
}
}
/// An error arising from processing events in an async executor event source.
#[derive(thiserror::Error, Debug)]
pub enum ExecutorError {
/// Error while reading new futures added via [`Scheduler::schedule()`].
#[error("error adding new futures")]
NewFutureError(ChannelError),
/// Error while processing wake events from existing futures.
#[error("error processing wake events")]
WakeError(PingError),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ready() {
let mut event_loop = crate::EventLoop::<u32>::try_new().unwrap();
let handle = event_loop.handle();
let (exec, sched) = executor::<u32>().unwrap();
handle
.insert_source(exec, move |ret, &mut (), got| {
*got = ret;
})
.unwrap();
let mut got = 0;
let fut = async { 42 };
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
.unwrap();
// the future is not yet inserted, and thus has not yet run
assert_eq!(got, 0);
sched.schedule(fut).unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut got)
.unwrap();
// the future has run
assert_eq!(got, 42);
}
}

View file

@ -0,0 +1,271 @@
//! A generic event source wrapping an IO objects or file descriptor
//!
//! You can use this general purpose adapter around file-descriptor backed objects to
//! insert into an [`EventLoop`](crate::EventLoop).
//!
//! The event generated by this [`Generic`] event source are the [`Readiness`](crate::Readiness)
//! notification itself, and the monitored object is provided to your callback as the second
//! argument.
//!
//! ```
//! # extern crate calloop;
//! use calloop::{generic::Generic, Interest, Mode, PostAction};
//!
//! # fn main() {
//! # let mut event_loop = calloop::EventLoop::<()>::try_new()
//! # .expect("Failed to initialize the event loop!");
//! # let handle = event_loop.handle();
//! # let io_object = 0;
//! handle.insert_source(
//! // wrap your IO object in a Generic, here we register for read readiness
//! // in level-triggering mode
//! Generic::new(io_object, Interest::READ, Mode::Level),
//! |readiness, io_object, shared_data| {
//! // The first argument of the callback is a Readiness
//! // The second is a &mut reference to your object
//!
//! // your callback needs to return a Result<PostAction, std::io::Error>
//! // if it returns an error, the event loop will consider this event
//! // event source as erroring and report it to the user.
//! Ok(PostAction::Continue)
//! }
//! );
//! # }
//! ```
//!
//! It can also help you implementing your own event sources: just have
//! these `Generic<_>` as fields of your event source, and delegate the
//! [`EventSource`](crate::EventSource) implementation to them.
//!
//! If you need to directly work with a [`RawFd`](std::os::unix::io::RawFd), rather than an
//! FD-backed object, see [`Generic::from_fd`](Generic#method.from_fd).
use std::{marker::PhantomData, os::unix::io::AsRawFd};
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
/// A generic event source wrapping a FD-backed type
#[derive(Debug)]
pub struct Generic<F: AsRawFd, E = std::io::Error> {
/// The wrapped FD-backed type
pub file: F,
/// The programmed interest
pub interest: Interest,
/// The programmed mode
pub mode: Mode,
// This token is used by the event loop logic to look up this source when an
// event occurs.
token: Option<Token>,
// This allows us to make the associated error and return types generic.
_error_type: PhantomData<E>,
}
impl<F: AsRawFd> Generic<F, std::io::Error> {
/// Wrap a FD-backed type into a `Generic` event source that uses
/// [`std::io::Error`] as its error type.
pub fn new(file: F, interest: Interest, mode: Mode) -> Generic<F, std::io::Error> {
Generic {
file,
interest,
mode,
token: None,
_error_type: PhantomData::default(),
}
}
/// Wrap a FD-backed type into a `Generic` event source using an arbitrary error type.
pub fn new_with_error<E>(file: F, interest: Interest, mode: Mode) -> Generic<F, E> {
Generic {
file,
interest,
mode,
token: None,
_error_type: PhantomData::default(),
}
}
}
impl<F: AsRawFd, E> Generic<F, E> {
/// Unwrap the `Generic` source to retrieve the underlying type
pub fn unwrap(self) -> F {
self.file
}
}
impl<F, E> EventSource for Generic<F, E>
where
F: AsRawFd,
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
type Event = Readiness;
type Metadata = F;
type Ret = Result<PostAction, E>;
type Error = E;
fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
// If the token is invalid or not ours, skip processing.
if self.token != Some(token) {
return Ok(PostAction::Continue);
}
callback(readiness, &mut self.file)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
let token = token_factory.token();
poll.register(self.file.as_raw_fd(), self.interest, self.mode, token)?;
self.token = Some(token);
Ok(())
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
let token = token_factory.token();
poll.reregister(self.file.as_raw_fd(), self.interest, self.mode, token)?;
self.token = Some(token);
Ok(())
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
poll.unregister(self.file.as_raw_fd())?;
self.token = None;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io::{Read, Write};
use super::Generic;
use crate::{Dispatcher, Interest, Mode, PostAction};
#[cfg(unix)]
#[test]
fn dispatch_unix() {
use std::os::unix::net::UnixStream;
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (mut tx, rx) = UnixStream::pair().unwrap();
let generic = Generic::new(rx, Interest::READ, Mode::Level);
let mut dispached = false;
let _generic_token = handle
.insert_source(generic, move |readiness, file, d| {
assert!(readiness.readable);
// we have not registered for writability
assert!(!readiness.writable);
let mut buffer = vec![0; 10];
let ret = file.read(&mut buffer).unwrap();
assert_eq!(ret, 6);
assert_eq!(&buffer[..6], &[1, 2, 3, 4, 5, 6]);
*d = true;
Ok(PostAction::Continue)
})
.unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
.unwrap();
assert!(!dispached);
let ret = tx.write(&[1, 2, 3, 4, 5, 6]).unwrap();
assert_eq!(ret, 6);
tx.flush().unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
.unwrap();
assert!(dispached);
}
#[test]
fn register_deregister_unix() {
use std::os::unix::net::UnixStream;
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let (mut tx, rx) = UnixStream::pair().unwrap();
let generic = Generic::new(rx, Interest::READ, Mode::Level);
let dispatcher = Dispatcher::new(generic, move |_, _, d| {
*d = true;
Ok(PostAction::Continue)
});
let mut dispached = false;
let generic_token = handle.register_dispatcher(dispatcher.clone()).unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
.unwrap();
assert!(!dispached);
// remove the source, and then write something
event_loop.handle().remove(generic_token);
let ret = tx.write(&[1, 2, 3, 4, 5, 6]).unwrap();
assert_eq!(ret, 6);
tx.flush().unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
.unwrap();
// the source has not been dispatched, as the source is no longer here
assert!(!dispached);
// insert it again
let generic = dispatcher.into_source_inner();
let _generic_token = handle
.insert_source(generic, move |readiness, file, d| {
assert!(readiness.readable);
// we have not registered for writability
assert!(!readiness.writable);
let mut buffer = vec![0; 10];
let ret = file.read(&mut buffer).unwrap();
assert_eq!(ret, 6);
assert_eq!(&buffer[..6], &[1, 2, 3, 4, 5, 6]);
*d = true;
Ok(PostAction::Continue)
})
.unwrap();
event_loop
.dispatch(Some(::std::time::Duration::ZERO), &mut dispached)
.unwrap();
// the has now been properly dispatched
assert!(dispached);
}
}

View file

@ -0,0 +1,714 @@
use std::{
cell::{Ref, RefCell, RefMut},
ops::{BitOr, BitOrAssign},
rc::Rc,
};
use crate::{sys::TokenFactory, Poll, Readiness, Token};
pub mod channel;
#[cfg(feature = "executor")]
#[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
pub mod futures;
pub mod generic;
pub mod ping;
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
pub mod signals;
pub mod timer;
pub mod transient;
/// Possible actions that can be requested to the event loop by an
/// event source once its events have been processed.
///
/// `PostAction` values can be combined with the `|` (bit-or) operator (or with
/// `|=`) with the result that:
/// - if both values are identical, the result is that value
/// - if they are different, the result is [`Reregister`](PostAction::Reregister)
///
/// Bit-or-ing these results is useful for composed sources to combine the
/// results of their child sources, but note that it only applies to the child
/// sources. For example, if every child source returns `Continue`, the result
/// will be `Continue`, but the parent source might still need to return
/// `Reregister` or something else depending on any additional logic it uses.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PostAction {
/// Continue listening for events on this source as before
Continue,
/// Trigger a re-registration of this source
Reregister,
/// Disable this source
///
/// Has the same effect as [`LoopHandle::disable`](crate::LoopHandle#method.disable)
Disable,
/// Remove this source from the eventloop
///
/// Has the same effect as [`LoopHandle::kill`](crate::LoopHandle#method.kill)
Remove,
}
/// Combines `PostAction` values returned from nested event sources.
impl BitOr for PostAction {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
if matches!(self, x if x == rhs) {
self
} else {
Self::Reregister
}
}
}
/// Combines `PostAction` values returned from nested event sources.
impl BitOrAssign for PostAction {
fn bitor_assign(&mut self, rhs: Self) {
if *self != rhs {
*self = Self::Reregister;
}
}
}
/// Trait representing an event source
///
/// This is the trait you need to implement if you wish to create your own
/// calloop-compatible event sources.
///
/// The 3 associated types define the type of closure the user will need to
/// provide to process events for your event source.
///
/// The `process_events` method will be called when one of the FD you registered
/// is ready, with the associated readiness and token.
///
/// The `register`, `reregister` and `unregister` methods are plumbing to let your
/// source register itself with the polling system. See their documentation for details.
///
/// In case your event source needs to do some special processing before or after a
/// polling session occurs (to prepare the underlying source for polling, and cleanup
/// after that), you can override the `pre_run` and `post_run`, that do nothing by
/// default. Depending on the underlying events, `process_events` may be invoked once,
/// several times, or none at all between `pre_run` and `post_run` are called, but when
/// it is invoked, it'll always be between those two.
pub trait EventSource {
/// The type of events generated by your source.
type Event;
/// Some metadata of your event source
///
/// This is typically useful if your source contains some internal state that
/// the user may need to interact with when processing events. The user callback
/// will receive a `&mut Metadata` reference.
///
/// Set to `()` if not needed.
type Metadata;
/// The return type of the user callback
///
/// If the user needs to return some value back to your event source once its
/// processing is finshed (to indicate success or failure for example), you can
/// specify it using this type.
///
/// Set to `()` if not needed.
type Ret;
/// The error type returned from
/// [`process_events()`](Self::process_events()) (not the user callback!).
type Error: Into<Box<dyn std::error::Error + Sync + Send>>;
/// Process any relevant events
///
/// This method will be called every time one of the FD you registered becomes
/// ready, including the readiness details and the associated token.
///
/// Your event source will then do some processing of the file descriptor(s) to generate
/// events, and call the provided `callback` for each one of them.
///
/// You should ensure you drained the file descriptors of their events, especially if using
/// edge-triggered mode.
fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret;
/// Register yourself to this poll instance
///
/// You should register all your relevant file descriptors to the provided [`Poll`](crate::Poll)
/// using its [`Poll::register`](crate::Poll#method.register) method.
///
/// If you need to register more than one file descriptor, you can change the
/// `sub_id` field of the [`Token`](crate::Token) to differentiate between them.
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()>;
/// Re-register your file descriptors
///
/// Your should update the registration of all your relevant file descriptor to
/// the provided [`Poll`](crate::Poll) using its [`Poll::reregister`](crate::Poll#method.reregister),
/// if necessary.
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()>;
/// Unregister your file descriptors
///
/// You should unregister all your file descriptors from this [`Poll`](crate::Poll) using its
/// [`Poll::unregister`](crate::Poll#method.unregister) method.
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()>;
/// Notification that a polling session is going to start
///
/// You can generate events from this method as you would from `process_events`.
fn pre_run<F>(&mut self, _callback: F) -> crate::Result<()>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Ok(())
}
/// Notification that the current polling session ended
///
/// You can generate events from this method as you would from `process_events`.
fn post_run<F>(&mut self, _callback: F) -> crate::Result<()>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Ok(())
}
}
/// Blanket implementation for boxed event sources. [`EventSource`] is not an
/// object safe trait, so this does not include trait objects.
impl<T: EventSource> EventSource for Box<T> {
type Event = T::Event;
type Metadata = T::Metadata;
type Ret = T::Ret;
type Error = T::Error;
fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
T::process_events(&mut **self, readiness, token, callback)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
T::register(&mut **self, poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
T::reregister(&mut **self, poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
T::unregister(&mut **self, poll)
}
fn pre_run<F>(&mut self, callback: F) -> crate::Result<()>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
T::pre_run(&mut **self, callback)
}
fn post_run<F>(&mut self, callback: F) -> crate::Result<()>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
T::post_run(&mut **self, callback)
}
}
/// Blanket implementation for exclusive references to event sources.
/// [`EventSource`] is not an object safe trait, so this does not include trait
/// objects.
impl<T: EventSource> EventSource for &mut T {
type Event = T::Event;
type Metadata = T::Metadata;
type Ret = T::Ret;
type Error = T::Error;
fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
T::process_events(&mut **self, readiness, token, callback)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
T::register(&mut **self, poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
T::reregister(&mut **self, poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
T::unregister(&mut **self, poll)
}
fn pre_run<F>(&mut self, callback: F) -> crate::Result<()>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
T::pre_run(&mut **self, callback)
}
fn post_run<F>(&mut self, callback: F) -> crate::Result<()>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
T::post_run(&mut **self, callback)
}
}
pub(crate) struct DispatcherInner<S, F> {
source: S,
callback: F,
}
impl<Data, S, F> EventDispatcher<Data> for RefCell<DispatcherInner<S, F>>
where
S: EventSource,
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret,
{
fn process_events(
&self,
readiness: Readiness,
token: Token,
data: &mut Data,
) -> crate::Result<PostAction> {
let mut disp = self.borrow_mut();
let DispatcherInner {
ref mut source,
ref mut callback,
} = *disp;
source
.process_events(readiness, token, |event, meta| callback(event, meta, data))
.map_err(|e| crate::Error::OtherError(e.into()))
}
fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.borrow_mut().source.register(poll, token_factory)
}
fn reregister(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<bool> {
if let Ok(mut me) = self.try_borrow_mut() {
me.source.reregister(poll, token_factory)?;
Ok(true)
} else {
Ok(false)
}
}
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool> {
if let Ok(mut me) = self.try_borrow_mut() {
me.source.unregister(poll)?;
Ok(true)
} else {
Ok(false)
}
}
fn pre_run(&self, data: &mut Data) -> crate::Result<()> {
let mut disp = self.borrow_mut();
let DispatcherInner {
ref mut source,
ref mut callback,
} = *disp;
source.pre_run(|event, meta| callback(event, meta, data))
}
fn post_run(&self, data: &mut Data) -> crate::Result<()> {
let mut disp = self.borrow_mut();
let DispatcherInner {
ref mut source,
ref mut callback,
} = *disp;
source.post_run(|event, meta| callback(event, meta, data))
}
}
pub(crate) trait EventDispatcher<Data> {
fn process_events(
&self,
readiness: Readiness,
token: Token,
data: &mut Data,
) -> crate::Result<PostAction>;
fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()>;
fn reregister(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<bool>;
fn unregister(&self, poll: &mut Poll) -> crate::Result<bool>;
fn pre_run(&self, data: &mut Data) -> crate::Result<()>;
fn post_run(&self, data: &mut Data) -> crate::Result<()>;
}
// An internal trait to erase the `F` type parameter of `DispatcherInner`
trait ErasedDispatcher<'a, S, Data> {
fn as_source_ref(&self) -> Ref<S>;
fn as_source_mut(&self) -> RefMut<S>;
fn into_source_inner(self: Rc<Self>) -> S;
fn into_event_dispatcher(self: Rc<Self>) -> Rc<dyn EventDispatcher<Data> + 'a>;
}
impl<'a, S, Data, F> ErasedDispatcher<'a, S, Data> for RefCell<DispatcherInner<S, F>>
where
S: EventSource + 'a,
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'a,
{
fn as_source_ref(&self) -> Ref<S> {
Ref::map(self.borrow(), |inner| &inner.source)
}
fn as_source_mut(&self) -> RefMut<S> {
RefMut::map(self.borrow_mut(), |inner| &mut inner.source)
}
fn into_source_inner(self: Rc<Self>) -> S {
if let Ok(ref_cell) = Rc::try_unwrap(self) {
ref_cell.into_inner().source
} else {
panic!("Dispatcher is still registered");
}
}
fn into_event_dispatcher(self: Rc<Self>) -> Rc<dyn EventDispatcher<Data> + 'a>
where
S: 'a,
{
self as Rc<dyn EventDispatcher<Data> + 'a>
}
}
/// An event source with its callback.
///
/// The `Dispatcher` can be registered in an event loop.
/// Use the `as_source_{ref,mut}` functions to interact with the event source.
/// Use `into_source_inner` to get the event source back.
pub struct Dispatcher<'a, S, Data>(Rc<dyn ErasedDispatcher<'a, S, Data> + 'a>);
impl<'a, S, Data> std::fmt::Debug for Dispatcher<'a, S, Data> {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Dispatcher { ... }")
}
}
impl<'a, S, Data> Dispatcher<'a, S, Data>
where
S: EventSource + 'a,
{
/// Builds a dispatcher.
///
/// The resulting `Dispatcher`
pub fn new<F>(source: S, callback: F) -> Self
where
F: FnMut(S::Event, &mut S::Metadata, &mut Data) -> S::Ret + 'a,
{
Dispatcher(Rc::new(RefCell::new(DispatcherInner { source, callback })))
}
/// Returns an immutable reference to the event source.
///
/// # Panics
///
/// Has the same semantics as `RefCell::borrow()`.
///
/// The dispatcher being mutably borrowed while its events are dispatched,
/// this method will panic if invoked from within the associated dispatching closure.
pub fn as_source_ref(&self) -> Ref<S> {
self.0.as_source_ref()
}
/// Returns a mutable reference to the event source.
///
/// # Panics
///
/// Has the same semantics as `RefCell::borrow_mut()`.
///
/// The dispatcher being mutably borrowed while its events are dispatched,
/// this method will panic if invoked from within the associated dispatching closure.
pub fn as_source_mut(&self) -> RefMut<S> {
self.0.as_source_mut()
}
/// Consumes the Dispatcher and returns the inner event source.
///
/// # Panics
///
/// Panics if the `Dispatcher` is still registered.
pub fn into_source_inner(self) -> S {
self.0.into_source_inner()
}
pub(crate) fn clone_as_event_dispatcher(&self) -> Rc<dyn EventDispatcher<Data> + 'a> {
Rc::clone(&self.0).into_event_dispatcher()
}
}
impl<'a, S, Data> Clone for Dispatcher<'a, S, Data> {
fn clone(&self) -> Dispatcher<'a, S, Data> {
Dispatcher(Rc::clone(&self.0))
}
}
/// An idle callback that was inserted in this loop
///
/// This handle allows you to cancel the callback. Dropping
/// it will *not* cancel it.
pub struct Idle<'i> {
pub(crate) callback: Rc<RefCell<dyn CancellableIdle + 'i>>,
}
impl<'i> std::fmt::Debug for Idle<'i> {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Idle { ... }")
}
}
impl<'i> Idle<'i> {
/// Cancel the idle callback if it was not already run
pub fn cancel(self) {
self.callback.borrow_mut().cancel();
}
}
pub(crate) trait CancellableIdle {
fn cancel(&mut self);
}
impl<F> CancellableIdle for Option<F> {
fn cancel(&mut self) {
self.take();
}
}
pub(crate) trait IdleDispatcher<Data> {
fn dispatch(&mut self, data: &mut Data);
}
impl<Data, F> IdleDispatcher<Data> for Option<F>
where
F: FnMut(&mut Data),
{
fn dispatch(&mut self, data: &mut Data) {
if let Some(callabck) = self.as_mut() {
callabck(data);
}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::{ping::make_ping, EventLoop};
// Test event source boxing.
#[test]
fn test_boxed_source() {
let mut fired = false;
let (pinger, source) = make_ping().unwrap();
let boxed = Box::new(source);
let mut event_loop = EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let token = handle
.insert_source(boxed, |_, _, fired| *fired = true)
.unwrap();
pinger.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert!(fired);
fired = false;
handle.update(&token).unwrap();
pinger.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert!(fired);
fired = false;
handle.remove(token);
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert!(!fired);
}
// Test event source trait methods via mut ref.
#[test]
fn test_mut_ref_source() {
let mut fired = false;
let (pinger, mut source) = make_ping().unwrap();
let source_ref = &mut source;
let mut event_loop = EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let token = handle
.insert_source(source_ref, |_, _, fired| *fired = true)
.unwrap();
pinger.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert!(fired);
fired = false;
handle.update(&token).unwrap();
pinger.ping();
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert!(fired);
fired = false;
handle.remove(token);
event_loop
.dispatch(Duration::new(0, 0), &mut fired)
.unwrap();
assert!(!fired);
}
// Test PostAction combinations.
#[test]
fn post_action_combine() {
use super::PostAction::*;
assert_eq!(Continue | Continue, Continue);
assert_eq!(Continue | Reregister, Reregister);
assert_eq!(Continue | Disable, Reregister);
assert_eq!(Continue | Remove, Reregister);
assert_eq!(Reregister | Continue, Reregister);
assert_eq!(Reregister | Reregister, Reregister);
assert_eq!(Reregister | Disable, Reregister);
assert_eq!(Reregister | Remove, Reregister);
assert_eq!(Disable | Continue, Reregister);
assert_eq!(Disable | Reregister, Reregister);
assert_eq!(Disable | Disable, Disable);
assert_eq!(Disable | Remove, Reregister);
assert_eq!(Remove | Continue, Reregister);
assert_eq!(Remove | Reregister, Reregister);
assert_eq!(Remove | Disable, Reregister);
assert_eq!(Remove | Remove, Remove);
}
// Test PostAction self-assignment.
#[test]
fn post_action_combine_assign() {
use super::PostAction::*;
let mut action = Continue;
action |= Continue;
assert_eq!(action, Continue);
let mut action = Continue;
action |= Reregister;
assert_eq!(action, Reregister);
let mut action = Continue;
action |= Disable;
assert_eq!(action, Reregister);
let mut action = Continue;
action |= Remove;
assert_eq!(action, Reregister);
let mut action = Reregister;
action |= Continue;
assert_eq!(action, Reregister);
let mut action = Reregister;
action |= Reregister;
assert_eq!(action, Reregister);
let mut action = Reregister;
action |= Disable;
assert_eq!(action, Reregister);
let mut action = Reregister;
action |= Remove;
assert_eq!(action, Reregister);
let mut action = Disable;
action |= Continue;
assert_eq!(action, Reregister);
let mut action = Disable;
action |= Reregister;
assert_eq!(action, Reregister);
let mut action = Disable;
action |= Disable;
assert_eq!(action, Disable);
let mut action = Disable;
action |= Remove;
assert_eq!(action, Reregister);
let mut action = Remove;
action |= Continue;
assert_eq!(action, Reregister);
let mut action = Remove;
action |= Reregister;
assert_eq!(action, Reregister);
let mut action = Remove;
action |= Disable;
assert_eq!(action, Reregister);
let mut action = Remove;
action |= Remove;
assert_eq!(action, Remove);
}
}

View file

@ -0,0 +1,290 @@
//! Ping to the event loop
//!
//! This is an event source that just produces `()` events whevener the associated
//! [`Ping::ping`](Ping#method.ping) method is called. If the event source is pinged multiple times
//! between a single dispatching, it'll only generate one event.
//!
//! This event source is a simple way of waking up the event loop from an other part of your program
//! (and is what backs the [`LoopSignal`](crate::LoopSignal)). It can also be used as a building
//! block to construct event sources whose source of event is not file descriptor, but rather an
//! userspace source (like an other thread).
use nix::unistd::close;
use std::os::unix::io::RawFd;
// The ping source has platform-dependent implementations provided by modules
// under this one. These modules should expose:
// - a make_ping() function
// - a Ping type
// - a PingSource type
//
// See eg. the pipe implementation for these items' specific requirements.
#[cfg(target_os = "linux")]
mod eventfd;
#[cfg(target_os = "linux")]
use eventfd as platform;
#[cfg(not(target_os = "linux"))]
mod pipe;
#[cfg(not(target_os = "linux"))]
use pipe as platform;
/// Create a new ping event source
///
/// you are given a [`Ping`] instance, which can be cloned and used to ping the
/// event loop, and a [`PingSource`], which you can insert in your event loop to
/// receive the pings.
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
platform::make_ping()
}
/// The ping event source
///
/// You can insert it in your event loop to receive pings.
///
/// If you use it directly, it will automatically remove itself from the event loop
/// once all [`Ping`] instances are dropped.
pub type Ping = platform::Ping;
/// The Ping handle
///
/// This handle can be cloned and sent accross threads. It can be used to
/// send pings to the `PingSource`.
pub type PingSource = platform::PingSource;
/// An error arising from processing events for a ping.
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct PingError(Box<dyn std::error::Error + Sync + Send>);
#[derive(Debug)]
struct CloseOnDrop(RawFd);
impl Drop for CloseOnDrop {
fn drop(&mut self) {
if let Err(e) = close(self.0) {
log::warn!("[calloop] Failed to close ping fd: {:?}", e);
}
}
}
#[cfg(test)]
mod tests {
use crate::transient::TransientSource;
use std::time::Duration;
use super::*;
#[test]
fn ping() {
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
let (ping, source) = make_ping().unwrap();
event_loop
.handle()
.insert_source(source, |(), &mut (), dispatched| *dispatched = true)
.unwrap();
ping.ping();
let mut dispatched = false;
event_loop
.dispatch(std::time::Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// Ping has been drained an no longer generates events
let mut dispatched = false;
event_loop
.dispatch(std::time::Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
}
#[test]
fn ping_closed() {
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
let (_, source) = make_ping().unwrap();
event_loop
.handle()
.insert_source(source, |(), &mut (), dispatched| *dispatched = true)
.unwrap();
let mut dispatched = false;
// If the sender is closed from the start, the ping should first trigger
// once, disabling itself but not invoking the callback
event_loop
.dispatch(std::time::Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
// Then it should not trigger any more, so this dispatch should wait the whole 100ms
let now = std::time::Instant::now();
event_loop
.dispatch(std::time::Duration::from_millis(100), &mut dispatched)
.unwrap();
assert!(now.elapsed() >= std::time::Duration::from_millis(100));
}
#[test]
fn ping_removed() {
// This keeps track of whether the event fired.
let mut dispatched = false;
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
let (sender, source) = make_ping().unwrap();
let wrapper = TransientSource::from(source);
// Check that the source starts off in the wrapper.
assert!(!matches!(wrapper, TransientSource::None));
// Put the source in the loop.
let dispatcher =
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
let token = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
// Drop the sender and check that it's actually removed.
drop(sender);
// There should be no event, but the loop still needs to wake up to
// process the close event (just like in the ping_closed() test).
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
// Pull the source wrapper out.
event_loop.handle().remove(token);
let wrapper = dispatcher.into_source_inner();
// Check that the inner source is now gone.
assert!(matches!(wrapper, TransientSource::None));
}
#[test]
fn ping_fired_and_removed() {
// This is like ping_removed() with the single difference that we fire a
// ping and drop it between two successive dispatches of the loop.
// This keeps track of whether the event fired.
let mut dispatched = false;
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
let (sender, source) = make_ping().unwrap();
let wrapper = TransientSource::from(source);
// Check that the source starts off in the wrapper.
assert!(!matches!(wrapper, TransientSource::None));
// Put the source in the loop.
let dispatcher =
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
let token = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
// Send a ping AND drop the sender and check that it's actually removed.
sender.ping();
drop(sender);
// There should be an event, but the source should be removed from the
// loop immediately after.
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// Pull the source wrapper out.
event_loop.handle().remove(token);
let wrapper = dispatcher.into_source_inner();
// Check that the inner source is now gone.
assert!(matches!(wrapper, TransientSource::None));
}
#[test]
fn ping_multiple_senders() {
// This is like ping_removed() but for testing the behaviour of multiple
// senders.
// This keeps track of whether the event fired.
let mut dispatched = false;
let mut event_loop = crate::EventLoop::<bool>::try_new().unwrap();
let (sender0, source) = make_ping().unwrap();
let wrapper = TransientSource::from(source);
let sender1 = sender0.clone();
let sender2 = sender1.clone();
// Check that the source starts off in the wrapper.
assert!(!matches!(wrapper, TransientSource::None));
// Put the source in the loop.
let dispatcher =
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
let token = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
// Send a ping AND drop the sender and check that it's actually removed.
sender0.ping();
drop(sender0);
// There should be an event, and the source should remain in the loop.
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// Now test that the clones still work. Drop after the dispatch loop
// instead of before, this time.
dispatched = false;
sender1.ping();
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
// Finally, drop all of them without sending anything.
dispatched = false;
drop(sender1);
drop(sender2);
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
// Pull the source wrapper out.
event_loop.handle().remove(token);
let wrapper = dispatcher.into_source_inner();
// Check that the inner source is now gone.
assert!(matches!(wrapper, TransientSource::None));
}
}

View file

@ -0,0 +1,189 @@
//! Eventfd based implementation of the ping event source.
//!
//! # Implementation notes
//!
//! The eventfd is a much lighter signalling mechanism provided by the Linux
//! kernel. Rather than write an arbitrary sequence of bytes, it only has a
//! 64-bit counter.
//!
//! To avoid closing the eventfd early, we wrap it in a RAII-style closer
//! `CloseOnDrop` in `make_ping()`. When all the senders are dropped, another
//! wrapper `FlagOnDrop` handles signalling this to the event source, which is
//! the sole owner of the eventfd itself. The senders have weak references to
//! the eventfd, and if the source is dropped before the senders, they will
//! simply not do anything (except log a message).
//!
//! To differentiate between regular ping events and close ping events, we add 2
//! to the counter for regular events and 1 for close events. In the source we
//! can then check the LSB and if it's set, we know it was a close event. This
//! only works if a close event never fires more than once.
use std::{os::unix::io::RawFd, sync::Arc};
use nix::sys::eventfd::{eventfd, EfdFlags};
use nix::unistd::{read, write};
use super::{CloseOnDrop, PingError};
use crate::{
generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory,
};
// These are not bitfields! They are increments to add to the eventfd counter.
// Since the fd can only be closed once, we can effectively use the
// INCREMENT_CLOSE value as a bitmask when checking.
const INCREMENT_PING: u64 = 0x2;
const INCREMENT_CLOSE: u64 = 0x1;
#[inline]
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
let read = eventfd(0, EfdFlags::EFD_CLOEXEC | EfdFlags::EFD_NONBLOCK)?;
// We only have one fd for the eventfd. If the sending end closes it when
// all copies are dropped, the receiving end will be closed as well. We need
// to make sure the fd is not closed until all holders of it have dropped
// it.
let fd_arc = Arc::new(CloseOnDrop(read));
let ping = Ping {
event: Arc::new(FlagOnDrop(Arc::clone(&fd_arc))),
};
let source = PingSource {
event: Generic::new(read, Interest::READ, Mode::Level),
_fd: fd_arc,
};
Ok((ping, source))
}
// Helper functions for the event source IO.
#[inline]
fn send_ping(fd: RawFd, count: u64) -> std::io::Result<()> {
assert!(count > 0);
match write(fd, &count.to_ne_bytes()) {
// The write succeeded, the ping will wake up the loop.
Ok(_) => Ok(()),
// The counter hit its cap, which means previous calls to write() will
// wake up the loop.
Err(nix::errno::Errno::EAGAIN) => Ok(()),
// Anything else is a real error.
Err(e) => Err(e.into()),
}
}
#[inline]
fn drain_ping(fd: RawFd) -> std::io::Result<u64> {
// The eventfd counter is effectively a u64.
const NBYTES: usize = 8;
let mut buf = [0u8; NBYTES];
match read(fd, &mut buf) {
// Reading from an eventfd should only ever produce 8 bytes. No looping
// is required.
Ok(NBYTES) => Ok(u64::from_ne_bytes(buf)),
Ok(_) => unreachable!(),
// Any other error can be propagated.
Err(e) => Err(e.into()),
}
}
// The event source is simply a generic source with one of the eventfds.
#[derive(Debug)]
pub struct PingSource {
event: Generic<RawFd>,
// This is held only to ensure that there is an owner of the fd that lives
// as long as the Generic source, so that the fd is not closed unexpectedly
// when all the senders are dropped.
_fd: Arc<CloseOnDrop>,
}
impl EventSource for PingSource {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = PingError;
fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.event
.process_events(readiness, token, |_, &mut fd| {
let counter = drain_ping(fd)?;
// If the LSB is set, it means we were closed. If anything else
// is also set, it means we were pinged. The two are not
// mutually exclusive.
let close = (counter & INCREMENT_CLOSE) != 0;
let ping = (counter & (u64::MAX - 1)) != 0;
if ping {
callback((), &mut ());
}
if close {
Ok(PostAction::Remove)
} else {
Ok(PostAction::Continue)
}
})
.map_err(|e| PingError(e.into()))
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.event.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.event.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.event.unregister(poll)
}
}
#[derive(Clone, Debug)]
pub struct Ping {
// This is an Arc because it's potentially shared with clones. The last one
// dropped needs to signal to the event source via the eventfd.
event: Arc<FlagOnDrop>,
}
impl Ping {
/// Send a ping to the `PingSource`.
pub fn ping(&self) {
if let Err(e) = send_ping(self.event.0 .0, INCREMENT_PING) {
log::warn!("[calloop] Failed to write a ping: {:?}", e);
}
}
}
/// This manages signalling to the PingSource when it's dropped. There should
/// only ever be one of these per PingSource.
#[derive(Debug)]
struct FlagOnDrop(Arc<CloseOnDrop>);
impl Drop for FlagOnDrop {
fn drop(&mut self) {
if let Err(e) = send_ping(self.0 .0, INCREMENT_CLOSE) {
log::warn!("[calloop] Failed to send close ping: {:?}", e);
}
}
}

View file

@ -0,0 +1,162 @@
//! Pipe based implementation of the ping event source, using the pipe or pipe2
//! syscall. Sending a ping involves writing to one end of a pipe, and the other
//! end becoming readable is what wakes up the event loop.
use std::{os::unix::io::RawFd, sync::Arc};
use nix::fcntl::OFlag;
use nix::unistd::{close, read, write};
use super::{CloseOnDrop, PingError};
use crate::{
generic::Generic, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory,
};
#[cfg(target_os = "macos")]
#[inline]
fn make_ends() -> std::io::Result<(RawFd, RawFd)> {
// macOS does not have pipe2, but we can emulate the behavior of pipe2 by
// setting the flags after calling pipe.
use nix::{
fcntl::{fcntl, FcntlArg},
unistd::pipe,
};
let (read, write) = pipe()?;
let read_flags = OFlag::from_bits_truncate(fcntl(read, FcntlArg::F_GETFD)?)
| OFlag::O_CLOEXEC
| OFlag::O_NONBLOCK;
let write_flags = OFlag::from_bits_truncate(fcntl(write, FcntlArg::F_GETFD)?)
| OFlag::O_CLOEXEC
| OFlag::O_NONBLOCK;
fcntl(read, FcntlArg::F_SETFL(read_flags))?;
fcntl(write, FcntlArg::F_SETFL(write_flags))?;
Ok((read, write))
}
#[cfg(not(target_os = "macos"))]
#[inline]
fn make_ends() -> std::io::Result<(RawFd, RawFd)> {
Ok(nix::unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK)?)
}
#[inline]
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
let (read, write) = make_ends()?;
let source = PingSource {
pipe: Generic::new(read, Interest::READ, Mode::Level),
};
let ping = Ping {
pipe: Arc::new(CloseOnDrop(write)),
};
Ok((ping, source))
}
// Helper functions for the event source IO.
#[inline]
fn send_ping(fd: RawFd) -> std::io::Result<()> {
write(fd, &[0u8])?;
Ok(())
}
// The event source is simply a generic source with the FD of the read end of
// the pipe.
#[derive(Debug)]
pub struct PingSource {
pipe: Generic<RawFd>,
}
impl EventSource for PingSource {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = PingError;
fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.pipe
.process_events(readiness, token, |_, &mut fd| {
let mut buf = [0u8; 32];
let mut read_something = false;
let mut action = PostAction::Continue;
loop {
match read(fd, &mut buf) {
Ok(0) => {
// The other end of the pipe was closed, mark ourselves
// for removal.
action = PostAction::Remove;
break;
}
// Got one or more pings.
Ok(_) => read_something = true,
// Nothing more to read.
Err(nix::errno::Errno::EAGAIN) => break,
// Propagate error.
Err(e) => return Err(e.into()),
}
}
if read_something {
callback((), &mut ());
}
Ok(action)
})
.map_err(|e| PingError(e.into()))
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.pipe.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.pipe.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.pipe.unregister(poll)
}
}
impl Drop for PingSource {
fn drop(&mut self) {
if let Err(e) = close(self.pipe.file) {
log::warn!("[calloop] Failed to close read ping: {:?}", e);
}
}
}
// The sending end of the ping writes zeroes to the write end of the pipe.
#[derive(Clone, Debug)]
pub struct Ping {
pipe: Arc<CloseOnDrop>,
}
// The sending end of the ping writes zeroes to the write end of the pipe.
impl Ping {
/// Send a ping to the `PingSource`
pub fn ping(&self) {
if let Err(e) = send_ping(self.pipe.0) {
log::warn!("[calloop] Failed to write a ping: {:?}", e);
}
}
}

View file

@ -0,0 +1,176 @@
//! Event source for tracking Unix signals
//!
//! Only available on Linux.
//!
//! This allows you to track and receive Unix signals through the event loop
//! rather than by registering signal handlers. It uses `signalfd` under the hood.
//!
//! The source will take care of masking and unmasking signals for the thread it runs on,
//! but you are responsible for masking them on other threads if you run them. The simplest
//! way to ensure that is to setup the signal event source before spawning any thread, as
//! they'll inherit their parent signal mask.
use std::convert::TryFrom;
use std::os::raw::c_int;
use nix::sys::signal::SigSet;
pub use nix::sys::signal::Signal;
pub use nix::sys::signalfd::siginfo;
use nix::sys::signalfd::{SfdFlags, SignalFd};
use super::generic::Generic;
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
/// An event generated by the signal event source
#[derive(Copy, Clone, Debug)]
pub struct Event {
info: siginfo,
}
impl Event {
/// Retrieve the signal number that was receive
pub fn signal(&self) -> Signal {
Signal::try_from(self.info.ssi_signo as c_int).unwrap()
}
/// Access the full `siginfo_t` associated with this signal event
pub fn full_info(&self) -> siginfo {
self.info
}
}
/// An event source for receiving Unix signals
#[derive(Debug)]
pub struct Signals {
sfd: Generic<SignalFd>,
mask: SigSet,
}
impl Signals {
/// Create a new signal event source listening on the specified list of signals
pub fn new(signals: &[Signal]) -> crate::Result<Signals> {
let mut mask = SigSet::empty();
for &s in signals {
mask.add(s);
}
// Mask the signals for this thread
mask.thread_block()?;
// Create the SignalFd
let sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK | SfdFlags::SFD_CLOEXEC)?;
Ok(Signals {
sfd: Generic::new(sfd, Interest::READ, Mode::Level),
mask,
})
}
/// Add a list of signals to the signals source
///
/// If this function returns an error, the signal mask of the thread may
/// have still been changed.
pub fn add_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
for &s in signals {
self.mask.add(s);
}
self.mask.thread_block()?;
self.sfd.file.set_mask(&self.mask)?;
Ok(())
}
/// Remove a list of signals from the signals source
///
/// If this function returns an error, the signal mask of the thread may
/// have still been changed.
pub fn remove_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
let mut removed = SigSet::empty();
for &s in signals {
self.mask.remove(s);
removed.add(s);
}
removed.thread_unblock()?;
self.sfd.file.set_mask(&self.mask)?;
Ok(())
}
/// Replace the list of signals of the source
///
/// If this function returns an error, the signal mask of the thread may
/// have still been changed.
pub fn set_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
let mut new_mask = SigSet::empty();
for &s in signals {
new_mask.add(s);
}
self.mask.thread_unblock()?;
new_mask.thread_block()?;
self.sfd.file.set_mask(&new_mask)?;
self.mask = new_mask;
Ok(())
}
}
impl Drop for Signals {
fn drop(&mut self) {
// we cannot handle error here
if let Err(e) = self.mask.thread_unblock() {
log::warn!("[calloop] Failed to unmask signals: {:?}", e);
}
}
}
impl EventSource for Signals {
type Event = Event;
type Metadata = ();
type Ret = ();
type Error = SignalError;
fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.sfd
.process_events(readiness, token, |_, sfd| {
loop {
match sfd.read_signal() {
Ok(Some(info)) => callback(Event { info }, &mut ()),
Ok(None) => break,
Err(e) => {
log::warn!("[callop] Error reading from signalfd: {}", e);
return Err(e.into());
}
}
}
Ok(PostAction::Continue)
})
.map_err(|e| SignalError(e.into()))
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.sfd.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.sfd.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.sfd.unregister(poll)
}
}
/// An error arising from processing events for a process signal.
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct SignalError(Box<dyn std::error::Error + Sync + Send>);

View file

@ -0,0 +1,571 @@
//! Timer event source
//!
//! The [`Timer`] is an event source that will fire its event after a certain amount of time
//! specified at creation. Its timing is tracked directly by the event loop core logic, and it does
//! not consume any system resource.
//!
//! The timer precision depends on whether the loop was initialized in high-precision mode. If not,
//! you can expect precision of order of 1 millisecond, if you need sub-millisecond precision,
//! make sure you initialize the [`EventLoop`](crate::EventLoop) using
//! [`EventLoop::try_new_high_precision()`](crate::EventLoop::try_new_high_precision). Note also
//! that if you need to rely on good precision timers in general, you may need to enable realtime
//! features of your OS to ensure your thread is quickly woken up by the system scheduler.
//!
//! The provided event is an [`Instant`] representing the deadline for which this timer has fired
//! (which can be earlier than the current time depending on the event loop congestion).
//!
//! The callback associated with this event source is expected to return a [`TimeoutAction`], which
//! can be used to implement self-repeating timers by telling calloop to reprogram the same timer
//! for a later timeout after it has fired.
/*
* This module provides two main types:
*
* - `Timer` is the user-facing type that represents a timer event source
* - `TimerWheel` is an internal data structure for tracking registered timeouts, it is used by
* the polling logic in sys/mod.rs
*/
use std::{
cell::RefCell,
collections::BinaryHeap,
rc::Rc,
task::Waker,
time::{Duration, Instant},
};
use crate::{EventSource, LoopHandle, Poll, PostAction, Readiness, Token, TokenFactory};
#[derive(Debug)]
struct Registration {
token: Token,
wheel: Rc<RefCell<TimerWheel>>,
counter: u32,
}
/// A timer event source
///
/// When registered to the event loop, it will trigger an event once its deadline is reached.
/// If the deadline is in the past relative to the moment of its insertion in the event loop,
/// the `TImer` will trigger an event as soon as the event loop is dispatched.
#[derive(Debug)]
pub struct Timer {
registration: Option<Registration>,
deadline: Instant,
}
impl Timer {
/// Create a timer that will fire immediately when inserted in the event loop
pub fn immediate() -> Timer {
Self::from_deadline(Instant::now())
}
/// Create a timer that will fire after a given duration from now
pub fn from_duration(duration: Duration) -> Timer {
Self::from_deadline(Instant::now() + duration)
}
/// Create a timer that will fire at a given instant
pub fn from_deadline(deadline: Instant) -> Timer {
Timer {
registration: None,
deadline,
}
}
/// Changes the deadline of this timer to an [`Instant`]
///
/// If the `Timer` is currently registered in the event loop, it needs to be
/// re-registered for this change to take effect.
pub fn set_deadline(&mut self, deadline: Instant) {
self.deadline = deadline;
}
/// Changes the deadline of this timer to a [`Duration`] from now
///
/// If the `Timer` is currently registered in the event loop, it needs to be
/// re-registered for this change to take effect.
pub fn set_duration(&mut self, duration: Duration) {
self.set_deadline(Instant::now() + duration)
}
/// Get the current deadline of this `Timer`
pub fn current_deadline(&self) -> Instant {
self.deadline
}
}
impl EventSource for Timer {
type Event = Instant;
type Metadata = ();
type Ret = TimeoutAction;
type Error = std::io::Error;
fn process_events<F>(
&mut self,
_: Readiness,
token: Token,
mut callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
if let Some(ref registration) = self.registration {
if registration.token != token {
return Ok(PostAction::Continue);
}
let new_deadline = match callback(self.deadline, &mut ()) {
TimeoutAction::Drop => return Ok(PostAction::Remove),
TimeoutAction::ToInstant(instant) => instant,
TimeoutAction::ToDuration(duration) => Instant::now() + duration,
};
// If we received an event, we MUST have a valid counter value
registration.wheel.borrow_mut().insert_reuse(
registration.counter,
new_deadline,
registration.token,
);
self.deadline = new_deadline;
}
Ok(PostAction::Continue)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
let wheel = poll.timers.clone();
let token = token_factory.token();
let counter = wheel.borrow_mut().insert(self.deadline, token);
self.registration = Some(Registration {
token,
wheel,
counter,
});
Ok(())
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.unregister(poll)?;
self.register(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
if let Some(registration) = self.registration.take() {
poll.timers.borrow_mut().cancel(registration.counter);
}
Ok(())
}
}
/// Action to reschedule a timeout if necessary
#[derive(Debug)]
pub enum TimeoutAction {
/// Don't reschedule this timer
Drop,
/// Reschedule this timer to a given [`Instant`]
ToInstant(Instant),
/// Reschedule this timer to a given [`Duration`] in the future
ToDuration(Duration),
}
// Internal representation of a timeout registered in the TimerWheel
#[derive(Debug)]
struct TimeoutData {
deadline: Instant,
token: RefCell<Option<Token>>,
counter: u32,
}
// A data structure for tracking registered timeouts
#[derive(Debug)]
pub(crate) struct TimerWheel {
heap: BinaryHeap<TimeoutData>,
counter: u32,
}
impl TimerWheel {
pub(crate) fn new() -> TimerWheel {
TimerWheel {
heap: BinaryHeap::new(),
counter: 0,
}
}
pub(crate) fn insert(&mut self, deadline: Instant, token: Token) -> u32 {
self.heap.push(TimeoutData {
deadline,
token: RefCell::new(Some(token)),
counter: self.counter,
});
let ret = self.counter;
self.counter += 1;
ret
}
pub(crate) fn insert_reuse(&mut self, counter: u32, deadline: Instant, token: Token) {
self.heap.push(TimeoutData {
deadline,
token: RefCell::new(Some(token)),
counter,
});
}
pub(crate) fn cancel(&mut self, counter: u32) {
self.heap
.iter()
.find(|data| data.counter == counter)
.map(|data| data.token.take());
}
pub(crate) fn next_expired(&mut self, now: Instant) -> Option<(u32, Token)> {
loop {
// check if there is an expired item
if let Some(data) = self.heap.peek() {
if data.deadline > now {
return None;
}
// there is an expired timeout, continue the
// loop body
} else {
return None;
}
// There is an item in the heap, this unwrap cannot blow
let data = self.heap.pop().unwrap();
if let Some(token) = data.token.into_inner() {
return Some((data.counter, token));
}
// otherwise this timeout was cancelled, continue looping
}
}
pub(crate) fn next_deadline(&self) -> Option<std::time::Instant> {
self.heap.peek().map(|data| data.deadline)
}
}
// trait implementations for TimeoutData
impl std::cmp::Ord for TimeoutData {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// earlier values have priority
self.deadline.cmp(&other.deadline).reverse()
}
}
impl std::cmp::PartialOrd for TimeoutData {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
// This impl is required for PartialOrd but actually never used
// and the type is private, so ignore its coverage
impl std::cmp::PartialEq for TimeoutData {
#[cfg_attr(coverage, no_coverage)]
fn eq(&self, other: &Self) -> bool {
self.deadline == other.deadline
}
}
impl std::cmp::Eq for TimeoutData {}
// Logic for timer futures
/// A future that resolves once a certain timeout is expired
pub struct TimeoutFuture {
deadline: Instant,
waker: Rc<RefCell<Option<Waker>>>,
}
impl std::fmt::Debug for TimeoutFuture {
#[cfg_attr(coverage, no_coverage)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TimeoutFuture")
.field("deadline", &self.deadline)
.finish_non_exhaustive()
}
}
impl TimeoutFuture {
/// Create a future that resolves after a given duration
pub fn from_duration<Data>(handle: &LoopHandle<'_, Data>, duration: Duration) -> TimeoutFuture {
Self::from_deadline(handle, Instant::now() + duration)
}
/// Create a future that resolves at a given instant
pub fn from_deadline<Data>(handle: &LoopHandle<'_, Data>, deadline: Instant) -> TimeoutFuture {
let timer = Timer::from_deadline(deadline);
let waker = Rc::new(RefCell::new(None::<Waker>));
let waker2 = waker.clone();
handle
.insert_source(timer, move |_, &mut (), _| {
if let Some(waker) = waker2.borrow_mut().clone() {
waker.wake()
}
TimeoutAction::Drop
})
.unwrap();
TimeoutFuture { deadline, waker }
}
}
impl std::future::Future for TimeoutFuture {
type Output = ();
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if std::time::Instant::now() >= self.deadline {
return std::task::Poll::Ready(());
}
*self.waker.borrow_mut() = Some(cx.waker().clone());
std::task::Poll::Pending
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
use std::time::Duration;
#[test]
fn simple_timer() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = false;
event_loop
.handle()
.insert_source(
Timer::from_duration(Duration::from_millis(100)),
|_, &mut (), dispatched| {
*dispatched = true;
TimeoutAction::Drop
},
)
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
// not yet dispatched
assert!(!dispatched);
event_loop
.dispatch(Some(Duration::from_millis(150)), &mut dispatched)
.unwrap();
// now dispatched
assert!(dispatched);
}
#[test]
fn simple_timer_instant() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = false;
event_loop
.handle()
.insert_source(
Timer::from_duration(Duration::from_millis(100)),
|_, &mut (), dispatched| {
*dispatched = true;
TimeoutAction::Drop
},
)
.unwrap();
event_loop
.dispatch(Some(Duration::from_millis(150)), &mut dispatched)
.unwrap();
// now dispatched
assert!(dispatched);
}
#[test]
fn immediate_timer() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = false;
event_loop
.handle()
.insert_source(Timer::immediate(), |_, &mut (), dispatched| {
*dispatched = true;
TimeoutAction::Drop
})
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
// now dispatched
assert!(dispatched);
}
// We cannot actually test high precision timers, as they are only high precision in release mode
// This test is here to ensure that the high-precision codepath are executed and work as intended
// even if we cannot test if they are actually high precision
#[test]
fn high_precision_timer() {
let mut event_loop = EventLoop::try_new_high_precision().unwrap();
let mut dispatched = false;
event_loop
.handle()
.insert_source(
Timer::from_duration(Duration::from_millis(100)),
|_, &mut (), dispatched| {
*dispatched = true;
TimeoutAction::Drop
},
)
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
// not yet dispatched
assert!(!dispatched);
event_loop
.dispatch(Some(Duration::from_micros(10200)), &mut dispatched)
.unwrap();
// yet not dispatched
assert!(!dispatched);
event_loop
.dispatch(Some(Duration::from_millis(100)), &mut dispatched)
.unwrap();
// now dispatched
assert!(dispatched);
}
#[test]
fn cancel_timer() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = false;
let token = event_loop
.handle()
.insert_source(
Timer::from_duration(Duration::from_millis(100)),
|_, &mut (), dispatched| {
*dispatched = true;
TimeoutAction::Drop
},
)
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
// not yet dispatched
assert!(!dispatched);
event_loop.handle().remove(token);
event_loop
.dispatch(Some(Duration::from_millis(150)), &mut dispatched)
.unwrap();
// still not dispatched
assert!(!dispatched);
}
#[test]
fn repeating_timer() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = 0;
event_loop
.handle()
.insert_source(
Timer::from_duration(Duration::from_millis(500)),
|_, &mut (), dispatched| {
*dispatched += 1;
TimeoutAction::ToDuration(Duration::from_millis(500))
},
)
.unwrap();
event_loop
.dispatch(Some(Duration::from_millis(250)), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 0);
event_loop
.dispatch(Some(Duration::from_millis(510)), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 1);
event_loop
.dispatch(Some(Duration::from_millis(510)), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 2);
event_loop
.dispatch(Some(Duration::from_millis(510)), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 3);
}
#[cfg(feature = "executor")]
#[test]
fn timeout_future() {
let mut event_loop = EventLoop::try_new().unwrap();
let mut dispatched = 0;
let timeout_1 =
TimeoutFuture::from_duration(&event_loop.handle(), Duration::from_millis(500));
let timeout_2 =
TimeoutFuture::from_duration(&event_loop.handle(), Duration::from_millis(1500));
let (exec, sched) = crate::sources::futures::executor().unwrap();
event_loop
.handle()
.insert_source(exec, move |(), &mut (), got| {
*got += 1;
})
.unwrap();
sched.schedule(timeout_1).unwrap();
sched.schedule(timeout_2).unwrap();
// We do a 0-timeout dispatch after every regular dispatch to let the timeout triggers
// flow back to the executor
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 0);
event_loop
.dispatch(Some(Duration::from_millis(1000)), &mut dispatched)
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 1);
event_loop
.dispatch(Some(Duration::from_millis(1100)), &mut dispatched)
.unwrap();
event_loop
.dispatch(Some(Duration::ZERO), &mut dispatched)
.unwrap();
assert_eq!(dispatched, 2);
}
}

View file

@ -0,0 +1,669 @@
//! Wrapper for a transient Calloop event source.
//!
//! If you have high level event source that you expect to remain in the event
//! loop indefinitely, and another event source nested inside that one that you
//! expect to require removal or disabling from time to time, this module can
//! handle it for you.
/// A [`TransientSource`] wraps a Calloop event source and manages its
/// registration. A user of this type only needs to perform the usual Calloop
/// calls (`process_events()` and `*register()`) and the return value of
/// [`process_events()`](crate::EventSource::process_events).
///
/// Rather than needing to check for the full set of
/// [`PostAction`](crate::PostAction) values returned from `process_events()`,
/// you can just check for `Continue` or `Reregister` and pass that back out
/// through your own `process_events()` implementation. In your registration
/// functions, you then only need to call the same function on this type ie.
/// `register()` inside `register()` etc.
///
/// For example, say you have a source that contains a channel along with some
/// other logic. If the channel's sending end has been dropped, it needs to be
/// removed from the loop. So to manage this, you use this in your struct:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// struct CompositeSource {
/// // Event source for channel.
/// mpsc_receiver: TransientSource<calloop::channel::Channel<T>>,
///
/// // Any other fields go here...
/// }
/// ```
///
/// To create the transient source, you can simply use the `Into`
/// implementation:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// let (sender, source) = channel();
/// let mpsc_receiver: TransientSource<Channel> = source.into();
/// ```
///
/// `TransientSource` implements [`EventSource`](crate::EventSource) and passes
/// through `process_events()` calls, so in the parent's `process_events()`
/// implementation you can just do this:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// fn process_events<F>(
/// &mut self,
/// readiness: calloop::Readiness,
/// token: calloop::Token,
/// callback: F,
/// ) -> Result<calloop::PostAction, Self::Error>
/// where
/// F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
/// {
/// let channel_return = self.mpsc_receiver.process_events(readiness, token, callback)?;
///
/// // Perform other logic here...
///
/// Ok(channel_return)
/// }
/// ```
///
/// Note that:
///
/// - You can call `process_events()` on the `TransientSource<Channel>` even
/// if the channel has been unregistered and dropped. All that will happen
/// is that you won't get any events from it.
///
/// - The [`PostAction`](crate::PostAction) returned from `process_events()`
/// will only ever be `PostAction::Continue` or `PostAction::Reregister`.
/// You will still need to combine this with the result of any other sources
/// (transient or not).
///
/// Once you return `channel_return` from your `process_events()` method (and
/// assuming it propagates all the way up to the event loop itself through any
/// other event sources), the event loop might call `reregister()` on your
/// source. All your source has to do is:
///
/// ```none,actually-rust-but-see-https://github.com/rust-lang/rust/issues/63193
/// fn reregister(
/// &mut self,
/// poll: &mut calloop::Poll,
/// token_factory: &mut calloop::TokenFactory,
/// ) -> crate::Result<()> {
/// self.mpsc_receiver.reregister(poll, token_factory)?;
///
/// // Other registration actions...
///
/// Ok(())
/// }
/// ```
///
/// The `TransientSource` will take care of updating the registration of the
/// inner source, even if it actually needs to be unregistered or initially
/// registered.
#[derive(Debug)]
pub enum TransientSource<T> {
/// The source should be kept in the loop.
Keep(T),
/// The source needs to be registered with the loop.
Register(T),
/// The source needs to be disabled but kept.
Disable(T),
/// The source needs to be removed from the loop.
Remove(T),
/// The source has been removed from the loop and dropped (this might also
/// be observed if there is a panic while changing states).
None,
}
impl<T> TransientSource<T> {
/// Apply a function to the enclosed source, if it exists. It will be
/// appplied even if the source is ready to be removed or is disabled.
pub fn map<F, U>(&mut self, f: F) -> Option<U>
where
F: FnOnce(&mut T) -> U,
{
match self {
TransientSource::Keep(source)
| TransientSource::Register(source)
| TransientSource::Disable(source)
| TransientSource::Remove(source) => Some(f(source)),
TransientSource::None => None,
}
}
/// If a caller needs to flag the contained source for removal or
/// registration, we need to replace the enum variant safely. This requires
/// having a `None` value in there temporarily while we do the swap.
///
/// If the variant is `None` the value will not change and `replacer` will
/// not be called.
///
/// The `replacer` function here is expected to be one of the enum variant
/// constructors eg. `replace(TransientSource::Remove)`.
fn replace<F>(&mut self, replacer: F)
where
F: FnOnce(T) -> Self,
{
*self = match std::mem::replace(self, TransientSource::None) {
TransientSource::Keep(source)
| TransientSource::Register(source)
| TransientSource::Remove(source)
| TransientSource::Disable(source) => replacer(source),
TransientSource::None => return,
};
}
}
impl<T: crate::EventSource> From<T> for TransientSource<T> {
fn from(source: T) -> Self {
Self::Register(source)
}
}
impl<T: crate::EventSource> crate::EventSource for TransientSource<T> {
type Event = T::Event;
type Metadata = T::Metadata;
type Ret = T::Ret;
type Error = T::Error;
fn process_events<F>(
&mut self,
readiness: crate::Readiness,
token: crate::Token,
callback: F,
) -> Result<crate::PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
let reregister = if let TransientSource::Keep(ref mut source) = self {
let child_post_action = source.process_events(readiness, token, callback)?;
match child_post_action {
// Nothing needs to change.
crate::PostAction::Continue => false,
// Our child source needs re-registration, therefore this
// wrapper needs re-registration.
crate::PostAction::Reregister => true,
// If our nested source needs to be removed or disabled, we need
// to swap it out for the "Remove" or "Disable" variant.
crate::PostAction::Disable => {
self.replace(TransientSource::Disable);
true
}
crate::PostAction::Remove => {
self.replace(TransientSource::Remove);
true
}
}
} else {
false
};
let post_action = if reregister {
crate::PostAction::Reregister
} else {
crate::PostAction::Continue
};
Ok(post_action)
}
fn register(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
match self {
TransientSource::Keep(source) => {
source.register(poll, token_factory)?;
}
TransientSource::Register(source) | TransientSource::Disable(source) => {
source.register(poll, token_factory)?;
self.replace(TransientSource::Keep);
}
TransientSource::Remove(_source) => {
*self = TransientSource::None;
}
TransientSource::None => (),
}
Ok(())
}
fn reregister(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
match self {
TransientSource::Keep(source) => source.reregister(poll, token_factory)?,
TransientSource::Register(source) => {
source.register(poll, token_factory)?;
self.replace(TransientSource::Keep);
}
TransientSource::Disable(source) => {
source.unregister(poll)?;
}
TransientSource::Remove(source) => {
source.unregister(poll)?;
*self = TransientSource::None;
}
TransientSource::None => (),
}
Ok(())
}
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
match self {
TransientSource::Keep(source)
| TransientSource::Register(source)
| TransientSource::Disable(source) => source.unregister(poll)?,
TransientSource::Remove(source) => {
source.unregister(poll)?;
*self = TransientSource::None;
}
TransientSource::None => (),
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
channel::{channel, Event},
ping::{make_ping, PingSource},
Dispatcher, EventSource, PostAction,
};
use std::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
#[test]
fn test_transient_drop() {
// A test source that sets a flag when it's dropped.
struct TestSource<'a> {
dropped: &'a AtomicBool,
ping: PingSource,
}
impl<'a> Drop for TestSource<'a> {
fn drop(&mut self) {
self.dropped.store(true, Ordering::Relaxed)
}
}
impl<'a> crate::EventSource for TestSource<'a> {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = Box<dyn std::error::Error + Sync + Send>;
fn process_events<F>(
&mut self,
readiness: crate::Readiness,
token: crate::Token,
callback: F,
) -> Result<crate::PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.ping.process_events(readiness, token, callback)?;
Ok(PostAction::Remove)
}
fn register(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.ping.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.ping.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
self.ping.unregister(poll)
}
}
// Test that the inner source is actually dropped when it asks to be
// removed from the loop, while the TransientSource remains. We use two
// flags for this:
// - fired: should be set only when the inner event source has an event
// - dropped: set by the drop handler for the inner source (it's an
// AtomicBool becaues it requires a longer lifetime than the fired
// flag)
let mut fired = false;
let dropped = false.into();
// The inner source that should be dropped after the first loop run.
let (pinger, ping) = make_ping().unwrap();
let inner = TestSource {
dropped: &dropped,
ping,
};
// The TransientSource wrapper.
let outer: TransientSource<_> = inner.into();
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let _token = handle
.insert_source(outer, |_, _, fired| {
*fired = true;
})
.unwrap();
// First loop run: the ping generates an event for the inner source.
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
assert!(fired);
assert!(dropped.load(Ordering::Relaxed));
// Second loop run: the ping does nothing because the receiver has been
// dropped.
fired = false;
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
assert!(!fired);
}
#[test]
fn test_transient_passthrough() {
// Test that event processing works when a source is nested inside a
// TransientSource. In particular, we want to ensure that the final
// event is received even if it corresponds to that same event source
// returning `PostAction::Remove`.
let (sender, receiver) = channel();
let outer: TransientSource<_> = receiver.into();
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
// Our callback puts the receied events in here for us to check later.
let mut msg_queue = vec![];
let _token = handle
.insert_source(outer, |msg, _, queue: &mut Vec<_>| {
queue.push(msg);
})
.unwrap();
// Send some data and drop the sender. We specifically want to test that
// we get the "closed" message.
sender.send(0u32).unwrap();
sender.send(1u32).unwrap();
sender.send(2u32).unwrap();
sender.send(3u32).unwrap();
drop(sender);
// Run loop once to process events.
event_loop.dispatch(Duration::ZERO, &mut msg_queue).unwrap();
assert!(matches!(
msg_queue.as_slice(),
&[
Event::Msg(0u32),
Event::Msg(1u32),
Event::Msg(2u32),
Event::Msg(3u32),
Event::Closed
]
));
}
#[test]
fn test_transient_map() {
struct IdSource {
id: u32,
ping: PingSource,
}
impl EventSource for IdSource {
type Event = u32;
type Metadata = ();
type Ret = ();
type Error = Box<dyn std::error::Error + Sync + Send>;
fn process_events<F>(
&mut self,
readiness: crate::Readiness,
token: crate::Token,
mut callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
let id = self.id;
self.ping
.process_events(readiness, token, |_, md| callback(id, md))?;
let action = if self.id > 2 {
PostAction::Remove
} else {
PostAction::Continue
};
Ok(action)
}
fn register(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.ping.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.ping.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
self.ping.unregister(poll)
}
}
struct WrapperSource(TransientSource<IdSource>);
impl EventSource for WrapperSource {
type Event = <IdSource as EventSource>::Event;
type Metadata = <IdSource as EventSource>::Metadata;
type Ret = <IdSource as EventSource>::Ret;
type Error = <IdSource as EventSource>::Error;
fn process_events<F>(
&mut self,
readiness: crate::Readiness,
token: crate::Token,
callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
let action = self.0.process_events(readiness, token, callback);
self.0.map(|inner| inner.id += 1);
action
}
fn register(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.0.map(|inner| inner.id += 1);
self.0.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.0.map(|inner| inner.id += 1);
self.0.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
self.0.map(|inner| inner.id += 1);
self.0.unregister(poll)
}
}
// To test the id later.
let mut id = 0;
// Create our source.
let (pinger, ping) = make_ping().unwrap();
let inner = IdSource { id, ping };
// The TransientSource wrapper.
let outer: TransientSource<_> = inner.into();
// The top level source.
let top = WrapperSource(outer);
// Create a dispatcher so we can check the source afterwards.
let dispatcher = Dispatcher::new(top, |got_id, _, test_id| {
*test_id = got_id;
});
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let token = handle.register_dispatcher(dispatcher.clone()).unwrap();
// First loop run: the ping generates an event for the inner source.
// The ID should be 1 after the increment in register().
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
assert_eq!(id, 1);
// Second loop run: the ID should be 2 after the previous
// process_events().
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
assert_eq!(id, 2);
// Third loop run: the ID should be 3 after another process_events().
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
assert_eq!(id, 3);
// Fourth loop run: the callback is no longer called by the inner
// source, so our local ID is not incremented.
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut id).unwrap();
assert_eq!(id, 3);
// Remove the dispatcher so we can inspect the sources.
handle.remove(token);
let mut top_after = dispatcher.into_source_inner();
// I expect the inner source to be dropped, so the TransientSource
// variant is None (its version of None, not Option::None), so its map()
// won't call the passed-in function (hence the unreachable!()) and its
// return value should be Option::None.
assert!(top_after.0.map(|_| unreachable!()).is_none());
}
#[test]
fn test_transient_disable() {
// Test that disabling and enabling is handled properly.
struct DisablingSource(PingSource);
impl EventSource for DisablingSource {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = Box<dyn std::error::Error + Sync + Send>;
fn process_events<F>(
&mut self,
readiness: crate::Readiness,
token: crate::Token,
callback: F,
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.0.process_events(readiness, token, callback)?;
Ok(PostAction::Disable)
}
fn register(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.0.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut crate::Poll,
token_factory: &mut crate::TokenFactory,
) -> crate::Result<()> {
self.0.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut crate::Poll) -> crate::Result<()> {
self.0.unregister(poll)
}
}
// Flag for checking when the source fires.
let mut fired = false;
// Create our source.
let (pinger, ping) = make_ping().unwrap();
let inner = DisablingSource(ping);
// The TransientSource wrapper.
let outer: TransientSource<_> = inner.into();
let mut event_loop = crate::EventLoop::try_new().unwrap();
let handle = event_loop.handle();
let token = handle
.insert_source(outer, |_, _, fired| {
*fired = true;
})
.unwrap();
// Ping here and not later, to check that disabling after an event is
// triggered but not processed does not discard the event.
pinger.ping();
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
assert!(fired);
// Source should now be disabled.
pinger.ping();
fired = false;
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
assert!(!fired);
// Re-enable the source.
handle.enable(&token).unwrap();
// Trigger another event.
pinger.ping();
fired = false;
event_loop.dispatch(Duration::ZERO, &mut fired).unwrap();
assert!(fired);
}
}

View 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);
}
}

View 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);
}
}

View 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))
}