Skip to content

Commit

Permalink
Address review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
DJMcNab committed Sep 2, 2023
1 parent ebcb8ab commit a62d85c
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 43 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

- Bump MSRV to 1.63
- Make signals an optional feature under the `signals` features.
- Replace the `nix` crate with standard library I/O errors and the `rustix`
crate.s
- Replace the `nix` crate with standard library I/O errors and the `rustix` crate.
- `pre_run` and `post_run` on `EventSource` have been replaced with `before_sleep` and `before_handle_events`, respectively.
These are now opt-in through the `NEEDS_EXTRA_LIFECYCLE_EVENTS` associated constant, and occur at slightly different times to
the methods they are replacing. This allows greater compatibility with Wayland based event sources.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ bitflags = "1.2"
futures-io = { version = "0.3.5", optional = true }
io-lifetimes = "1.0.3"
log = "0.4"
nix = { version = "0.26", default-features = false, features = ["signal",], optional = true }
nix = { version = "0.26", default-features = false, features = ["signal"], optional = true }
pin-utils = { version = "0.1.0", optional = true }
polling = "2.6.0"
rustix = { version = "0.38", default-features = false, features = ["event", "fs", "pipe", "std"] }
Expand Down
3 changes: 2 additions & 1 deletion src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
#[cfg(feature = "futures-io")]
use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut};

use crate::loop_logic::EventIterator;
use crate::{
loop_logic::{LoopInner, MAX_SOURCES_MASK},
sources::EventDispatcher,
Expand Down Expand Up @@ -274,7 +275,7 @@ impl<Data> EventDispatcher<Data> for RefCell<IoDispatcher> {
fn before_sleep(&self) -> crate::Result<Option<(Readiness, Token)>> {
Ok(None)

Check warning on line 276 in src/io.rs

View check run for this annotation

Codecov / codecov/patch

src/io.rs#L275-L276

Added lines #L275 - L276 were not covered by tests
}
fn before_handle_events(&self, _: bool) {}
fn before_handle_events(&self, _: EventIterator<'_>) {}

Check warning on line 278 in src/io.rs

View check run for this annotation

Codecov / codecov/patch

src/io.rs#L278

Added line #L278 was not covered by tests
}

/*
Expand Down
61 changes: 43 additions & 18 deletions src/loop_logic.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::cell::{Cell, RefCell};
use std::fmt::Debug;
use std::io;
use std::iter::Chain;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::{io, slice};

#[cfg(feature = "block_on")]
use std::future::Future;
Expand All @@ -15,7 +16,8 @@ use slab::Slab;
use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher};
use crate::sys::{Notifier, PollEvent};
use crate::{
AdditionalLifecycleEventsSet, EventDispatcher, InsertError, Poll, PostAction, TokenFactory,
AdditionalLifecycleEventsSet, EventDispatcher, InsertError, Poll, PostAction, Readiness, Token,
TokenFactory,
};

type IdleCallback<'i, Data> = Rc<RefCell<dyn IdleDispatcher<Data> + 'i>>;
Expand Down Expand Up @@ -342,8 +344,7 @@ impl<'l, Data> EventLoop<'l, Data> {
.sources_with_additional_lifecycle_events
.borrow_mut();
let sources = &self.handle.inner.sources.borrow();
for (source, has_event) in &mut *extra_lifecycle_sources.values {
*has_event = false;
for source in &mut *extra_lifecycle_sources.values {
if let Some(disp) = sources.get(source.key) {
if let Some((readiness, token)) = disp.before_sleep()? {
// Wake up instantly after polling if we recieved an event
Expand Down Expand Up @@ -384,19 +385,18 @@ impl<'l, Data> EventLoop<'l, Data> {
.sources_with_additional_lifecycle_events
.borrow_mut();
if !extra_lifecycle_sources.values.is_empty() {
for (source, has_event) in &mut *extra_lifecycle_sources.values {
for event in &events {
*has_event |= (event.token.key & MAX_SOURCES_MASK) == source.key;
for source in &mut *extra_lifecycle_sources.values {
if let Some(disp) = self.handle.inner.sources.borrow().get(source.key) {
let iter = EventIterator {
inner: self.synthetic_events.iter().chain(&events),
registration_token: *source,
};
disp.before_handle_events(iter);
} else {
unreachable!()

Check warning on line 396 in src/loop_logic.rs

View check run for this annotation

Codecov / codecov/patch

src/loop_logic.rs#L396

Added line #L396 was not covered by tests
}
}
}
for (source, has_event) in &*extra_lifecycle_sources.values {
if let Some(disp) = self.handle.inner.sources.borrow().get(source.key) {
disp.before_handle_events(*has_event);
} else {
unreachable!()
}
}
}

for event in self.synthetic_events.drain(..).chain(events) {
Expand Down Expand Up @@ -626,6 +626,31 @@ impl<'l, Data> EventLoop<'l, Data> {
}
}

#[derive(Clone, Debug)]
/// The EventIterator is an `Iterator` over the events relevant to a particular source
/// This type is used in the [`EventSource::before_handle_events`] methods for
/// two main reasons:
/// - To avoid dynamic dispatch overhead
/// - Secondly, it is to allow this type to be `Clone`, which is not
/// possible with dynamic dispatch
pub struct EventIterator<'a> {
inner: Chain<slice::Iter<'a, PollEvent>, slice::Iter<'a, PollEvent>>,
registration_token: RegistrationToken,
}

impl<'a> Iterator for EventIterator<'a> {
type Item = (Readiness, Token);

fn next(&mut self) -> Option<Self::Item> {
while let Some(next) = self.inner.next() {

Check failure on line 645 in src/loop_logic.rs

View workflow job for this annotation

GitHub Actions / lint

this loop could be written as a `for` loop
if next.token.key & MAX_SOURCES_MASK == self.registration_token.key {
return Some((next.readiness, next.token));
}

Check warning on line 648 in src/loop_logic.rs

View check run for this annotation

Codecov / codecov/patch

src/loop_logic.rs#L644-L648

Added lines #L644 - L648 were not covered by tests
}
None
}

Check warning on line 651 in src/loop_logic.rs

View check run for this annotation

Codecov / codecov/patch

src/loop_logic.rs#L650-L651

Added lines #L650 - L651 were not covered by tests
}

/// A signal that can be shared between thread to stop or wakeup a running
/// event loop
#[derive(Clone)]
Expand Down Expand Up @@ -671,8 +696,8 @@ mod tests {
channel::{channel, Channel},
generic::Generic,
ping::*,
Dispatcher, EventSource, Interest, Mode, Poll, PostAction, Readiness, RegistrationToken,
Token, TokenFactory,
Dispatcher, EventIterator, EventSource, Interest, Mode, Poll, PostAction, Readiness,
RegistrationToken, Token, TokenFactory,
};

use super::EventLoop;
Expand Down Expand Up @@ -859,7 +884,7 @@ mod tests {
Ok(None)
}

fn before_handle_events(&mut self, _: bool) {
fn before_handle_events(&mut self, _: EventIterator) {
self.lock.unlock();
}
}
Expand Down Expand Up @@ -1015,7 +1040,7 @@ mod tests {
Ok(Some((Readiness::EMPTY, self.token.unwrap())))
}

fn before_handle_events(&mut self, _: bool) {
fn before_handle_events(&mut self, _: EventIterator) {
self.lock.unlock();
}
}
Expand Down
50 changes: 29 additions & 21 deletions src/sources/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
rc::Rc,
};

pub use crate::loop_logic::EventIterator;
use crate::{sys::TokenFactory, Poll, Readiness, RegistrationToken, Token};

pub mod channel;
Expand Down Expand Up @@ -168,24 +169,34 @@ pub trait EventSource {
/// and [`EventSource::before_handle_events`] notifications. These are opt-in because
/// they require more expensive checks, and almost all sources will not need these notifications
const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = false;
/// Notification that a single `poll` is about to begin.
/// If this returns Ok(Some), polling will shortcircuit, and
/// your event handler will be called with the returned Token and Readiness
/// Notification that a single `poll` is about to begin
///
/// Use this to perform operations which must be done before polling,
/// but which may conflict with other event handlers. For example,
/// if polling requires a lock to be taken.
/// if polling requires a lock to be taken
///
/// If this returns Ok(Some), this will be treated as an event arriving in polling, and
/// your event handler will be called with the returned `Token` and `Readiness`.
/// Polling will however still occur, so additional events from this or other sources
/// may also be handled in the same loop.
/// The returned `Token` must belong to this source
// If you need to return multiple synthetic events from this notification, please
// open an issue
fn before_sleep(&mut self) -> crate::Result<Option<(Readiness, Token)>> {
Ok(None)
}
/// Notification that polling is completed, and the resulting events are
/// going to be executed.
/// Notification that polling is complete, and [`EventSource::process_events`] will
/// be called with the given events for this source. The iterator may be empty,
/// which indicates that no events were generated for this source
///
/// Please note, the iterator will also include any synthetic event returned from
/// [`EventSource::before_sleep`]
///
/// Use this to perform a cleanup before event handlers with arbitrary
/// code may run. This could be used to drop a lock obtained in
/// [`EventSource::before_sleep`]
#[allow(unused_variables)]
fn before_handle_events(&mut self, was_awoken: bool) {}
fn before_handle_events(&mut self, events: EventIterator<'_>) {}
}

/// Blanket implementation for boxed event sources. [`EventSource`] is not an
Expand Down Expand Up @@ -230,8 +241,8 @@ impl<T: EventSource> EventSource for Box<T> {
T::before_sleep(&mut **self)
}

fn before_handle_events(&mut self, was_awoken: bool) {
T::before_handle_events(&mut **self, was_awoken)
fn before_handle_events(&mut self, events: EventIterator) {
T::before_handle_events(&mut **self, events)
}
}

Expand Down Expand Up @@ -278,8 +289,8 @@ impl<T: EventSource> EventSource for &mut T {
T::before_sleep(&mut **self)
}

fn before_handle_events(&mut self, was_awoken: bool) {
T::before_handle_events(&mut **self, was_awoken)
fn before_handle_events(&mut self, events: EventIterator) {
T::before_handle_events(&mut **self, events)
}
}

Expand Down Expand Up @@ -365,10 +376,10 @@ where
source.before_sleep()
}

fn before_handle_events(&self, was_awoken: bool) {
fn before_handle_events(&self, events: EventIterator<'_>) {
let mut disp = self.borrow_mut();
let DispatcherInner { ref mut source, .. } = *disp;
source.before_handle_events(was_awoken);
source.before_handle_events(events);
}
}

Expand Down Expand Up @@ -402,26 +413,23 @@ pub(crate) trait EventDispatcher<Data> {
) -> crate::Result<bool>;

fn before_sleep(&self) -> crate::Result<Option<(Readiness, Token)>>;
fn before_handle_events(&self, was_awoken: bool);
fn before_handle_events(&self, events: EventIterator<'_>);
}

#[derive(Default)]
/// The list of events
pub(crate) struct AdditionalLifecycleEventsSet {
/// The list of events. The boolean indicates whether the source had an event
/// - this is stored in this list because we need to know this value for each
/// item at once - that is, to avoid allocating in the hot loop
pub(crate) values: Vec<(RegistrationToken, bool)>,
/// The list of sources
pub(crate) values: Vec<RegistrationToken>,
}

impl AdditionalLifecycleEventsSet {
fn register(&mut self, token: RegistrationToken) {
self.values.push((token, false))
self.values.push(token)
}

fn unregister(&mut self, token: RegistrationToken) {
self.values
.retain(|it: &(RegistrationToken, bool)| it.0 != token)
self.values.retain(|it| it != &token)
}
}

Expand Down

0 comments on commit a62d85c

Please sign in to comment.