1688 lines
54 KiB
Rust
1688 lines
54 KiB
Rust
use std::cell::{Cell, RefCell};
|
|
use std::fmt::Debug;
|
|
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;
|
|
|
|
#[cfg(unix)]
|
|
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
|
#[cfg(windows)]
|
|
use std::os::windows::io::{AsHandle, AsRawHandle, AsSocket as AsFd, BorrowedHandle, RawHandle};
|
|
|
|
use log::trace;
|
|
use polling::Poller;
|
|
|
|
use crate::list::{SourceEntry, SourceList};
|
|
use crate::sources::{Dispatcher, EventSource, Idle, IdleDispatcher};
|
|
use crate::sys::{Notifier, PollEvent};
|
|
use crate::token::TokenInner;
|
|
use crate::{
|
|
AdditionalLifecycleEventsSet, InsertError, Poll, PostAction, Readiness, Token, TokenFactory,
|
|
};
|
|
|
|
type IdleCallback<'i, Data> = Rc<RefCell<dyn IdleDispatcher<Data> + 'i>>;
|
|
|
|
/// 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(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub struct RegistrationToken {
|
|
inner: TokenInner,
|
|
}
|
|
|
|
impl RegistrationToken {
|
|
/// Create the RegistrationToken corresponding to the given raw key
|
|
/// This is needed because some methods use `RegistrationToken`s as
|
|
/// raw usizes within this crate
|
|
pub(crate) fn new(inner: TokenInner) -> Self {
|
|
Self { inner }
|
|
}
|
|
}
|
|
|
|
pub(crate) struct LoopInner<'l, Data> {
|
|
pub(crate) poll: RefCell<Poll>,
|
|
// The `Option` is used to keep slots of the slab occipied, to prevent id reuse
|
|
// while in-flight events might still referr to a recently destroyed event source.
|
|
pub(crate) sources: RefCell<SourceList<'l, Data>>,
|
|
pub(crate) sources_with_additional_lifecycle_events: RefCell<AdditionalLifecycleEventsSet>,
|
|
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(feature = "nightly_coverage", coverage(off))]
|
|
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(feature = "nightly_coverage", coverage(off))]
|
|
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`.
|
|
#[cfg_attr(feature = "nightly_coverage", coverage(off))] // Contains a branch we can't hit w/o OOM
|
|
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();
|
|
|
|
// Find an empty slot if any
|
|
let slot = sources.vacant_entry();
|
|
|
|
slot.source = Some(dispatcher.clone_as_event_dispatcher());
|
|
trace!("[calloop] Inserting new source #{}", slot.token.get_id());
|
|
let ret = slot.source.as_ref().unwrap().register(
|
|
&mut poll,
|
|
&mut self
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
&mut TokenFactory::new(slot.token),
|
|
);
|
|
|
|
if let Err(error) = ret {
|
|
slot.source = None;
|
|
return Err(error);
|
|
}
|
|
|
|
Ok(RegistrationToken { inner: slot.token })
|
|
}
|
|
|
|
/// 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 &SourceEntry {
|
|
token: entry_token,
|
|
source: Some(ref source),
|
|
} = self.inner.sources.borrow().get(token.inner)?
|
|
{
|
|
trace!("[calloop] Registering source #{}", entry_token.get_id());
|
|
source.register(
|
|
&mut self.inner.poll.borrow_mut(),
|
|
&mut self
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
&mut TokenFactory::new(entry_token),
|
|
)
|
|
} else {
|
|
Err(crate::Error::InvalidToken)
|
|
}
|
|
}
|
|
|
|
/// 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 &SourceEntry {
|
|
token: entry_token,
|
|
source: Some(ref source),
|
|
} = self.inner.sources.borrow().get(token.inner)?
|
|
{
|
|
trace!(
|
|
"[calloop] Updating registration of source #{}",
|
|
entry_token.get_id()
|
|
);
|
|
if !source.reregister(
|
|
&mut self.inner.poll.borrow_mut(),
|
|
&mut self
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
&mut TokenFactory::new(entry_token),
|
|
)? {
|
|
trace!("[calloop] Cannot do it now, storing for later.");
|
|
// we are in a callback, store for later processing
|
|
self.inner.pending_action.set(PostAction::Reregister);
|
|
}
|
|
Ok(())
|
|
} else {
|
|
Err(crate::Error::InvalidToken)
|
|
}
|
|
}
|
|
|
|
/// 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 &SourceEntry {
|
|
token: entry_token,
|
|
source: Some(ref source),
|
|
} = self.inner.sources.borrow().get(token.inner)?
|
|
{
|
|
if !token.inner.same_source_as(entry_token) {
|
|
// The token provided by the user is no longer valid
|
|
return Err(crate::Error::InvalidToken);
|
|
}
|
|
trace!("[calloop] Unregistering source #{}", entry_token.get_id());
|
|
if !source.unregister(
|
|
&mut self.inner.poll.borrow_mut(),
|
|
&mut self
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
*token,
|
|
)? {
|
|
trace!("[calloop] Cannot do it now, storing for later.");
|
|
// we are in a callback, store for later processing
|
|
self.inner.pending_action.set(PostAction::Disable);
|
|
}
|
|
Ok(())
|
|
} else {
|
|
Err(crate::Error::InvalidToken)
|
|
}
|
|
}
|
|
|
|
/// Removes this source from the event loop.
|
|
pub fn remove(&self, token: RegistrationToken) {
|
|
if let Ok(&mut SourceEntry {
|
|
token: entry_token,
|
|
ref mut source,
|
|
}) = self.inner.sources.borrow_mut().get_mut(token.inner)
|
|
{
|
|
if let Some(source) = source.take() {
|
|
trace!("[calloop] Removing source #{}", entry_token.get_id());
|
|
if let Err(e) = source.unregister(
|
|
&mut self.inner.poll.borrow_mut(),
|
|
&mut self
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
token,
|
|
) {
|
|
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: AsFd>(&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> {
|
|
#[allow(dead_code)]
|
|
poller: Arc<Poller>,
|
|
handle: LoopHandle<'l, Data>,
|
|
signals: Arc<Signals>,
|
|
// A caching vector for synthetic poll events
|
|
synthetic_events: Vec<PollEvent>,
|
|
}
|
|
|
|
impl<'l, Data> std::fmt::Debug for EventLoop<'l, Data> {
|
|
#[cfg_attr(feature = "nightly_coverage", coverage(off))]
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str("EventLoop { ... }")
|
|
}
|
|
}
|
|
|
|
/// Signals related to the event loop.
|
|
struct Signals {
|
|
/// Signal to stop the event loop.
|
|
stop: AtomicBool,
|
|
|
|
/// Signal that the future is ready.
|
|
#[cfg(feature = "block_on")]
|
|
future_ready: AtomicBool,
|
|
}
|
|
|
|
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> {
|
|
let poll = Poll::new()?;
|
|
let poller = poll.poller.clone();
|
|
let handle = LoopHandle {
|
|
inner: Rc::new(LoopInner {
|
|
poll: RefCell::new(poll),
|
|
sources: RefCell::new(SourceList::new()),
|
|
idles: RefCell::new(Vec::new()),
|
|
pending_action: Cell::new(PostAction::Continue),
|
|
sources_with_additional_lifecycle_events: Default::default(),
|
|
}),
|
|
};
|
|
|
|
Ok(EventLoop {
|
|
handle,
|
|
signals: Arc::new(Signals {
|
|
stop: AtomicBool::new(false),
|
|
#[cfg(feature = "block_on")]
|
|
future_ready: AtomicBool::new(false),
|
|
}),
|
|
poller,
|
|
synthetic_events: vec![],
|
|
})
|
|
}
|
|
|
|
/// 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 mut extra_lifecycle_sources = self
|
|
.handle
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut();
|
|
let sources = &self.handle.inner.sources.borrow();
|
|
for source in &mut *extra_lifecycle_sources.values {
|
|
if let Ok(SourceEntry {
|
|
source: Some(disp), ..
|
|
}) = sources.get(source.inner)
|
|
{
|
|
if let Some((readiness, token)) = disp.before_sleep()? {
|
|
// Wake up instantly after polling if we recieved an event
|
|
timeout = Some(Duration::ZERO);
|
|
self.synthetic_events.push(PollEvent { readiness, token });
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
let events = {
|
|
let poll = self.handle.inner.poll.borrow();
|
|
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),
|
|
};
|
|
}
|
|
};
|
|
{
|
|
let mut extra_lifecycle_sources = self
|
|
.handle
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut();
|
|
if !extra_lifecycle_sources.values.is_empty() {
|
|
for source in &mut *extra_lifecycle_sources.values {
|
|
if let Ok(SourceEntry {
|
|
source: Some(disp), ..
|
|
}) = self.handle.inner.sources.borrow().get(source.inner)
|
|
{
|
|
let iter = EventIterator {
|
|
inner: events.iter(),
|
|
registration_token: *source,
|
|
};
|
|
disp.before_handle_events(iter);
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for event in self.synthetic_events.drain(..).chain(events) {
|
|
// Get the registration token associated with the event.
|
|
let reg_token = event.token.inner.forget_sub_id();
|
|
|
|
let opt_disp = self
|
|
.handle
|
|
.inner
|
|
.sources
|
|
.borrow()
|
|
.get(reg_token)
|
|
.ok()
|
|
.and_then(|entry| entry.source.clone());
|
|
|
|
if let Some(disp) = opt_disp {
|
|
trace!(
|
|
"[calloop] Dispatching events for source #{}",
|
|
reg_token.get_id()
|
|
);
|
|
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 => {
|
|
trace!(
|
|
"[calloop] Postaction reregister for source #{}",
|
|
reg_token.get_id()
|
|
);
|
|
disp.reregister(
|
|
&mut self.handle.inner.poll.borrow_mut(),
|
|
&mut self
|
|
.handle
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
&mut TokenFactory::new(reg_token),
|
|
)?;
|
|
}
|
|
PostAction::Disable => {
|
|
trace!(
|
|
"[calloop] Postaction unregister for source #{}",
|
|
reg_token.get_id()
|
|
);
|
|
disp.unregister(
|
|
&mut self.handle.inner.poll.borrow_mut(),
|
|
&mut self
|
|
.handle
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
RegistrationToken::new(reg_token),
|
|
)?;
|
|
}
|
|
PostAction::Remove => {
|
|
trace!(
|
|
"[calloop] Postaction remove for source #{}",
|
|
reg_token.get_id()
|
|
);
|
|
if let Ok(entry) = self.handle.inner.sources.borrow_mut().get_mut(reg_token)
|
|
{
|
|
entry.source = None;
|
|
}
|
|
}
|
|
PostAction::Continue => {}
|
|
}
|
|
|
|
if self
|
|
.handle
|
|
.inner
|
|
.sources
|
|
.borrow()
|
|
.get(reg_token)
|
|
.ok()
|
|
.map(|entry| entry.source.is_none())
|
|
.unwrap_or(true)
|
|
{
|
|
// 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,
|
|
&mut self
|
|
.handle
|
|
.inner
|
|
.sources_with_additional_lifecycle_events
|
|
.borrow_mut(),
|
|
RegistrationToken::new(reg_token),
|
|
) {
|
|
log::warn!(
|
|
"[calloop] Failed to unregister source from the polling system: {:?}",
|
|
e
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
log::warn!(
|
|
"[calloop] Received an event for non-existence source: {:?}",
|
|
reg_token
|
|
);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// 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.dispatch_events(timeout.into(), data)?;
|
|
self.dispatch_idles(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.signals.clone(),
|
|
notifier: self.handle.inner.poll.borrow().notifier(),
|
|
}
|
|
}
|
|
|
|
/// 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.signals.stop.store(false, Ordering::Release);
|
|
while !self.signals.stop.load(Ordering::Acquire) {
|
|
self.dispatch(timeout, data)?;
|
|
cb(data);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Block a future on this event loop.
|
|
///
|
|
/// This will run the provided future on this event loop, blocking until it is
|
|
/// resolved.
|
|
///
|
|
/// If [`LoopSignal::stop()`] is called before the future is resolved, this function returns
|
|
/// `None`.
|
|
#[cfg(feature = "block_on")]
|
|
pub fn block_on<R>(
|
|
&mut self,
|
|
future: impl Future<Output = R>,
|
|
data: &mut Data,
|
|
mut cb: impl FnMut(&mut Data),
|
|
) -> crate::Result<Option<R>> {
|
|
use std::task::{Context, Poll, Wake, Waker};
|
|
|
|
/// A waker that will wake up the event loop when it is ready to make progress.
|
|
struct EventLoopWaker(LoopSignal);
|
|
|
|
impl Wake for EventLoopWaker {
|
|
fn wake(self: Arc<Self>) {
|
|
// Set the waker.
|
|
self.0.signal.future_ready.store(true, Ordering::Release);
|
|
self.0.notifier.notify().ok();
|
|
}
|
|
|
|
fn wake_by_ref(self: &Arc<Self>) {
|
|
// Set the waker.
|
|
self.0.signal.future_ready.store(true, Ordering::Release);
|
|
self.0.notifier.notify().ok();
|
|
}
|
|
}
|
|
|
|
// Pin the future to the stack.
|
|
pin_utils::pin_mut!(future);
|
|
|
|
// Create a waker that will wake up the event loop when it is ready to make progress.
|
|
let waker = {
|
|
let handle = EventLoopWaker(self.get_signal());
|
|
|
|
Waker::from(Arc::new(handle))
|
|
};
|
|
let mut context = Context::from_waker(&waker);
|
|
|
|
// Begin running the loop.
|
|
let mut output = None;
|
|
|
|
self.signals.stop.store(false, Ordering::Release);
|
|
self.signals.future_ready.store(true, Ordering::Release);
|
|
|
|
while !self.signals.stop.load(Ordering::Acquire) {
|
|
// If the future is ready to be polled, poll it.
|
|
if self.signals.future_ready.swap(false, Ordering::AcqRel) {
|
|
// Poll the future and break the loop if it's ready.
|
|
if let Poll::Ready(result) = future.as_mut().poll(&mut context) {
|
|
output = Some(result);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Otherwise, block on the event loop.
|
|
self.dispatch_events(None, data)?;
|
|
self.dispatch_idles(data);
|
|
cb(data);
|
|
}
|
|
|
|
Ok(output)
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
impl<'l, Data> AsRawFd for EventLoop<'l, Data> {
|
|
/// Get the underlying raw-fd of the poller.
|
|
///
|
|
/// This could be used to create [`Generic`] source out of the current loop
|
|
/// and inserting into some other [`EventLoop`]. It's recommended to clone `fd`
|
|
/// before doing so.
|
|
///
|
|
/// [`Generic`]: crate::generic::Generic
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
self.poller.as_raw_fd()
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
impl<'l, Data> AsFd for EventLoop<'l, Data> {
|
|
/// Get the underlying fd of the poller.
|
|
///
|
|
/// This could be used to create [`Generic`] source out of the current loop
|
|
/// and inserting into some other [`EventLoop`].
|
|
///
|
|
/// [`Generic`]: crate::generic::Generic
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
|
self.poller.as_fd()
|
|
}
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
impl<Data> AsRawHandle for EventLoop<'_, Data> {
|
|
fn as_raw_handle(&self) -> RawHandle {
|
|
self.poller.as_raw_handle()
|
|
}
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
impl<Data> AsHandle for EventLoop<'_, Data> {
|
|
fn as_handle(&self) -> BorrowedHandle<'_> {
|
|
self.poller.as_handle()
|
|
}
|
|
}
|
|
|
|
#[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: slice::Iter<'a, PollEvent>,
|
|
registration_token: RegistrationToken,
|
|
}
|
|
|
|
impl<'a> Iterator for EventIterator<'a> {
|
|
type Item = (Readiness, Token);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
for next in self.inner.by_ref() {
|
|
if next
|
|
.token
|
|
.inner
|
|
.same_source_as(self.registration_token.inner)
|
|
{
|
|
return Some((next.readiness, next.token));
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
/// A signal that can be shared between thread to stop or wakeup a running
|
|
/// event loop
|
|
#[derive(Clone)]
|
|
pub struct LoopSignal {
|
|
signal: Arc<Signals>,
|
|
notifier: Notifier,
|
|
}
|
|
|
|
impl std::fmt::Debug for LoopSignal {
|
|
#[cfg_attr(feature = "nightly_coverage", coverage(off))]
|
|
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 useful if you are using the `EventLoop::run()` method.
|
|
pub fn stop(&self) {
|
|
self.signal.stop.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.notifier.notify().ok();
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::{cell::Cell, rc::Rc, time::Duration};
|
|
|
|
use crate::{
|
|
channel::{channel, Channel},
|
|
ping::*,
|
|
EventIterator, EventSource, Poll, PostAction, Readiness, RegistrationToken, Token,
|
|
TokenFactory,
|
|
};
|
|
|
|
#[cfg(unix)]
|
|
use crate::{generic::Generic, Dispatcher, Interest, Mode};
|
|
|
|
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 additional_events() {
|
|
let mut event_loop: EventLoop<'_, Lock> = EventLoop::try_new().unwrap();
|
|
let mut lock = Lock {
|
|
lock: Rc::new((
|
|
// Whether the lock is locked
|
|
Cell::new(false),
|
|
// The total number of events processed in process_events
|
|
Cell::new(0),
|
|
// The total number of events processed in before_handle_events
|
|
// This is used to ensure that the count seen in before_handle_events is expected
|
|
Cell::new(0),
|
|
)),
|
|
};
|
|
let (sender, channel) = channel();
|
|
let token = event_loop
|
|
.handle()
|
|
.insert_source(
|
|
LockingSource {
|
|
channel,
|
|
lock: lock.clone(),
|
|
},
|
|
|_, _, lock| {
|
|
lock.lock();
|
|
lock.unlock();
|
|
},
|
|
)
|
|
.unwrap();
|
|
sender.send(()).unwrap();
|
|
|
|
event_loop.dispatch(None, &mut lock).unwrap();
|
|
// We should have been locked twice so far
|
|
assert_eq!(lock.lock.1.get(), 2);
|
|
// And we should have received one event
|
|
assert_eq!(lock.lock.2.get(), 1);
|
|
event_loop.handle().disable(&token).unwrap();
|
|
event_loop
|
|
.dispatch(Some(Duration::ZERO), &mut lock)
|
|
.unwrap();
|
|
assert_eq!(lock.lock.1.get(), 2);
|
|
|
|
event_loop.handle().enable(&token).unwrap();
|
|
event_loop
|
|
.dispatch(Some(Duration::ZERO), &mut lock)
|
|
.unwrap();
|
|
assert_eq!(lock.lock.1.get(), 3);
|
|
event_loop.handle().remove(token);
|
|
event_loop
|
|
.dispatch(Some(Duration::ZERO), &mut lock)
|
|
.unwrap();
|
|
assert_eq!(lock.lock.1.get(), 3);
|
|
assert_eq!(lock.lock.2.get(), 1);
|
|
|
|
#[derive(Clone)]
|
|
struct Lock {
|
|
lock: Rc<(Cell<bool>, Cell<u32>, Cell<u32>)>,
|
|
}
|
|
impl Lock {
|
|
fn lock(&self) {
|
|
if self.lock.0.get() {
|
|
panic!();
|
|
}
|
|
// Increase the count
|
|
self.lock.1.set(self.lock.1.get() + 1);
|
|
self.lock.0.set(true)
|
|
}
|
|
fn unlock(&self) {
|
|
if !self.lock.0.get() {
|
|
panic!();
|
|
}
|
|
self.lock.0.set(false);
|
|
}
|
|
}
|
|
struct LockingSource {
|
|
channel: Channel<()>,
|
|
lock: Lock,
|
|
}
|
|
impl EventSource for LockingSource {
|
|
type Event = <Channel<()> as EventSource>::Event;
|
|
|
|
type Metadata = <Channel<()> as EventSource>::Metadata;
|
|
|
|
type Ret = <Channel<()> as EventSource>::Ret;
|
|
|
|
type Error = <Channel<()> as EventSource>::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,
|
|
{
|
|
self.channel.process_events(readiness, token, callback)
|
|
}
|
|
|
|
fn register(
|
|
&mut self,
|
|
poll: &mut Poll,
|
|
token_factory: &mut TokenFactory,
|
|
) -> crate::Result<()> {
|
|
self.channel.register(poll, token_factory)
|
|
}
|
|
|
|
fn reregister(
|
|
&mut self,
|
|
poll: &mut Poll,
|
|
token_factory: &mut TokenFactory,
|
|
) -> crate::Result<()> {
|
|
self.channel.reregister(poll, token_factory)
|
|
}
|
|
|
|
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
|
self.channel.unregister(poll)
|
|
}
|
|
|
|
const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true;
|
|
|
|
fn before_sleep(&mut self) -> crate::Result<Option<(Readiness, Token)>> {
|
|
self.lock.lock();
|
|
Ok(None)
|
|
}
|
|
|
|
fn before_handle_events(&mut self, events: EventIterator) {
|
|
let events_count = events.count();
|
|
let lock = &self.lock.lock;
|
|
lock.2.set(lock.2.get() + events_count as u32);
|
|
self.lock.unlock();
|
|
}
|
|
}
|
|
}
|
|
#[test]
|
|
fn default_additional_events() {
|
|
let (sender, channel) = channel();
|
|
let mut test_source = NoopWithDefaultHandlers { channel };
|
|
let mut event_loop = EventLoop::try_new().unwrap();
|
|
event_loop
|
|
.handle()
|
|
.insert_source(Box::new(&mut test_source), |_, _, _| {})
|
|
.unwrap();
|
|
sender.send(()).unwrap();
|
|
|
|
event_loop.dispatch(None, &mut ()).unwrap();
|
|
struct NoopWithDefaultHandlers {
|
|
channel: Channel<()>,
|
|
}
|
|
impl EventSource for NoopWithDefaultHandlers {
|
|
type Event = <Channel<()> as EventSource>::Event;
|
|
|
|
type Metadata = <Channel<()> as EventSource>::Metadata;
|
|
|
|
type Ret = <Channel<()> as EventSource>::Ret;
|
|
|
|
type Error = <Channel<()> as EventSource>::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,
|
|
{
|
|
self.channel.process_events(readiness, token, callback)
|
|
}
|
|
|
|
fn register(
|
|
&mut self,
|
|
poll: &mut Poll,
|
|
token_factory: &mut TokenFactory,
|
|
) -> crate::Result<()> {
|
|
self.channel.register(poll, token_factory)
|
|
}
|
|
|
|
fn reregister(
|
|
&mut self,
|
|
poll: &mut Poll,
|
|
token_factory: &mut TokenFactory,
|
|
) -> crate::Result<()> {
|
|
self.channel.reregister(poll, token_factory)
|
|
}
|
|
|
|
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
|
|
self.channel.unregister(poll)
|
|
}
|
|
|
|
const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn additional_events_synthetic() {
|
|
let mut event_loop: EventLoop<'_, Lock> = EventLoop::try_new().unwrap();
|
|
let mut lock = Lock {
|
|
lock: Rc::new(Cell::new(false)),
|
|
};
|
|
event_loop
|
|
.handle()
|
|
.insert_source(
|
|
InstantWakeupLockingSource {
|
|
lock: lock.clone(),
|
|
token: None,
|
|
},
|
|
|_, _, lock| {
|
|
lock.lock();
|
|
lock.unlock();
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
// Loop should finish, as
|
|
event_loop.dispatch(None, &mut lock).unwrap();
|
|
#[derive(Clone)]
|
|
struct Lock {
|
|
lock: Rc<Cell<bool>>,
|
|
}
|
|
impl Lock {
|
|
fn lock(&self) {
|
|
if self.lock.get() {
|
|
panic!();
|
|
}
|
|
self.lock.set(true)
|
|
}
|
|
fn unlock(&self) {
|
|
if !self.lock.get() {
|
|
panic!();
|
|
}
|
|
self.lock.set(false);
|
|
}
|
|
}
|
|
struct InstantWakeupLockingSource {
|
|
lock: Lock,
|
|
token: Option<Token>,
|
|
}
|
|
impl EventSource for InstantWakeupLockingSource {
|
|
type Event = ();
|
|
|
|
type Metadata = ();
|
|
|
|
type Ret = ();
|
|
|
|
type Error = <Channel<()> as EventSource>::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,
|
|
{
|
|
assert_eq!(token, self.token.unwrap());
|
|
callback((), &mut ());
|
|
Ok(PostAction::Continue)
|
|
}
|
|
|
|
fn register(
|
|
&mut self,
|
|
_: &mut Poll,
|
|
token_factory: &mut TokenFactory,
|
|
) -> crate::Result<()> {
|
|
self.token = Some(token_factory.token());
|
|
Ok(())
|
|
}
|
|
|
|
fn reregister(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
|
|
unreachable!()
|
|
}
|
|
|
|
fn unregister(&mut self, _: &mut Poll) -> crate::Result<()> {
|
|
unreachable!()
|
|
}
|
|
|
|
const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true;
|
|
|
|
fn before_sleep(&mut self) -> crate::Result<Option<(Readiness, Token)>> {
|
|
self.lock.lock();
|
|
Ok(Some((Readiness::EMPTY, self.token.unwrap())))
|
|
}
|
|
|
|
fn before_handle_events(&mut self, _: EventIterator) {
|
|
self.lock.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn insert_bad_source() {
|
|
use std::os::unix::io::FromRawFd;
|
|
|
|
let event_loop = EventLoop::<()>::try_new().unwrap();
|
|
let fd = unsafe { std::os::unix::io::OwnedFd::from_raw_fd(420) };
|
|
let ret = event_loop.handle().insert_source(
|
|
crate::sources::generic::Generic::new(fd, Interest::READ, Mode::Level),
|
|
|_, _, _| Ok(PostAction::Continue),
|
|
);
|
|
assert!(ret.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_token() {
|
|
let (_ping, source) = crate::sources::ping::make_ping().unwrap();
|
|
|
|
let event_loop = EventLoop::<()>::try_new().unwrap();
|
|
let handle = event_loop.handle();
|
|
let reg_token = handle.insert_source(source, |_, _, _| {}).unwrap();
|
|
handle.remove(reg_token);
|
|
|
|
let ret = handle.enable(®_token);
|
|
assert!(ret.is_err());
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn insert_source_no_interest() {
|
|
use rustix::pipe::pipe;
|
|
|
|
// Create a pipe to get an arbitrary fd.
|
|
let (read, _write) = pipe().unwrap();
|
|
|
|
let source = crate::sources::generic::Generic::new(read, Interest::EMPTY, Mode::Level);
|
|
let dispatcher = Dispatcher::new(source, |_, _, _| Ok(PostAction::Continue));
|
|
|
|
let event_loop = EventLoop::<()>::try_new().unwrap();
|
|
let handle = event_loop.handle();
|
|
let ret = handle.register_dispatcher(dispatcher.clone());
|
|
|
|
if let Ok(token) = ret {
|
|
// Unwrap the dispatcher+source and close the read end.
|
|
handle.remove(token);
|
|
} else {
|
|
// Fail the test.
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
#[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);
|
|
|
|
// 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);
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn change_interests() {
|
|
use rustix::io::write;
|
|
use rustix::net::{recv, socketpair, AddressFamily, RecvFlags, SocketFlags, SocketType};
|
|
let mut event_loop = EventLoop::<bool>::try_new().unwrap();
|
|
|
|
let (sock1, sock2) = socketpair(
|
|
AddressFamily::UNIX,
|
|
SocketType::STREAM,
|
|
SocketFlags::empty(),
|
|
None, // 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, |_, fd, dispatched| {
|
|
*dispatched = true;
|
|
// read all contents available to drain the socket
|
|
let mut buf = [0u8; 32];
|
|
loop {
|
|
match recv(&*fd, &mut buf, RecvFlags::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);
|
|
}
|
|
|
|
#[cfg(feature = "block_on")]
|
|
#[test]
|
|
fn block_on_test() {
|
|
use crate::sources::timer::TimeoutFuture;
|
|
use std::time::Duration;
|
|
|
|
let mut evl = EventLoop::<()>::try_new().unwrap();
|
|
|
|
let mut data = 22;
|
|
let timeout = {
|
|
let data = &mut data;
|
|
let evl_handle = evl.handle();
|
|
|
|
async move {
|
|
TimeoutFuture::from_duration(&evl_handle, Duration::from_secs(2)).await;
|
|
*data = 32;
|
|
11
|
|
}
|
|
};
|
|
|
|
let result = evl.block_on(timeout, &mut (), |&mut ()| {}).unwrap();
|
|
assert_eq!(result, Some(11));
|
|
assert_eq!(data, 32);
|
|
}
|
|
|
|
#[cfg(feature = "block_on")]
|
|
#[test]
|
|
fn block_on_early_cancel() {
|
|
use crate::sources::timer;
|
|
use std::time::Duration;
|
|
|
|
let mut evl = EventLoop::<()>::try_new().unwrap();
|
|
|
|
let mut data = 22;
|
|
let timeout = {
|
|
let data = &mut data;
|
|
let evl_handle = evl.handle();
|
|
|
|
async move {
|
|
timer::TimeoutFuture::from_duration(&evl_handle, Duration::from_secs(2)).await;
|
|
*data = 32;
|
|
11
|
|
}
|
|
};
|
|
|
|
let timer_source = timer::Timer::from_duration(Duration::from_secs(1));
|
|
let handle = evl.get_signal();
|
|
let _timer_token = evl
|
|
.handle()
|
|
.insert_source(timer_source, move |_, _, _| {
|
|
handle.stop();
|
|
timer::TimeoutAction::Drop
|
|
})
|
|
.unwrap();
|
|
|
|
let result = evl.block_on(timeout, &mut (), |&mut ()| {}).unwrap();
|
|
assert_eq!(result, None);
|
|
assert_eq!(data, 22);
|
|
}
|
|
|
|
#[test]
|
|
fn reuse() {
|
|
use crate::sources::timer;
|
|
use std::sync::{Arc, Mutex};
|
|
use std::time::{Duration, Instant};
|
|
|
|
let mut evl = EventLoop::<RegistrationToken>::try_new().unwrap();
|
|
let handle = evl.handle();
|
|
|
|
let data = Arc::new(Mutex::new(1));
|
|
let data_cloned = data.clone();
|
|
|
|
let timer_source = timer::Timer::from_duration(Duration::from_secs(1));
|
|
let mut first_timer_token = evl
|
|
.handle()
|
|
.insert_source(timer_source, move |_, _, own_token| {
|
|
handle.remove(*own_token);
|
|
let data_cloned = data_cloned.clone();
|
|
let _ = handle.insert_source(timer::Timer::immediate(), move |_, _, _| {
|
|
*data_cloned.lock().unwrap() = 2;
|
|
timer::TimeoutAction::Drop
|
|
});
|
|
timer::TimeoutAction::Drop
|
|
})
|
|
.unwrap();
|
|
|
|
let now = Instant::now();
|
|
loop {
|
|
evl.dispatch(Some(Duration::from_secs(3)), &mut first_timer_token)
|
|
.unwrap();
|
|
if Instant::now().duration_since(now) > Duration::from_secs(3) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert_eq!(*data.lock().unwrap(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn drop_of_subsource() {
|
|
struct WithSubSource {
|
|
token: Option<Token>,
|
|
}
|
|
|
|
impl crate::EventSource for WithSubSource {
|
|
type Event = ();
|
|
type Metadata = ();
|
|
type Ret = ();
|
|
type Error = crate::Error;
|
|
const NEEDS_EXTRA_LIFECYCLE_EVENTS: bool = true;
|
|
|
|
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 ());
|
|
// Drop the source
|
|
Ok(PostAction::Remove)
|
|
}
|
|
|
|
fn register(&mut self, _: &mut Poll, fact: &mut TokenFactory) -> crate::Result<()> {
|
|
// produce a few tokens to emulate a subsource
|
|
fact.token();
|
|
fact.token();
|
|
self.token = Some(fact.token());
|
|
Ok(())
|
|
}
|
|
|
|
fn reregister(&mut self, _: &mut Poll, _: &mut TokenFactory) -> crate::Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
fn unregister(&mut self, _: &mut Poll) -> crate::Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
// emulate a readiness
|
|
fn before_sleep(&mut self) -> crate::Result<Option<(Readiness, Token)>> {
|
|
Ok(self.token.map(|token| {
|
|
(
|
|
Readiness {
|
|
readable: true,
|
|
writable: false,
|
|
error: false,
|
|
},
|
|
token,
|
|
)
|
|
}))
|
|
}
|
|
}
|
|
|
|
// Now the actual test
|
|
let mut evl = EventLoop::<bool>::try_new().unwrap();
|
|
evl.handle()
|
|
.insert_source(WithSubSource { token: None }, |_, _, ran| {
|
|
*ran = true;
|
|
})
|
|
.unwrap();
|
|
|
|
let mut ran = false;
|
|
|
|
evl.dispatch(Some(Duration::ZERO), &mut ran).unwrap();
|
|
|
|
assert!(ran);
|
|
}
|
|
|
|
// 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(())
|
|
}
|
|
}
|
|
}
|