use nix::unistd::close;
use std::os::unix::io::RawFd;
#[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;
pub fn make_ping() -> std::io::Result<(Ping, PingSource)> {
platform::make_ping()
}
pub type Ping = platform::Ping;
pub type PingSource = platform::PingSource;
#[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);
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;
event_loop
.dispatch(std::time::Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
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() {
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);
assert!(!matches!(wrapper, TransientSource::None));
let dispatcher =
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
let token = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
drop(sender);
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
event_loop.handle().remove(token);
let wrapper = dispatcher.into_source_inner();
assert!(matches!(wrapper, TransientSource::None));
}
#[test]
fn ping_fired_and_removed() {
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);
assert!(!matches!(wrapper, TransientSource::None));
let dispatcher =
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
let token = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
sender.ping();
drop(sender);
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
event_loop.handle().remove(token);
let wrapper = dispatcher.into_source_inner();
assert!(matches!(wrapper, TransientSource::None));
}
#[test]
fn ping_multiple_senders() {
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();
assert!(!matches!(wrapper, TransientSource::None));
let dispatcher =
crate::Dispatcher::new(wrapper, |(), &mut (), dispatched| *dispatched = true);
let token = event_loop
.handle()
.register_dispatcher(dispatcher.clone())
.unwrap();
sender0.ping();
drop(sender0);
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
dispatched = false;
sender1.ping();
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(dispatched);
dispatched = false;
drop(sender1);
drop(sender2);
event_loop
.dispatch(Duration::ZERO, &mut dispatched)
.unwrap();
assert!(!dispatched);
event_loop.handle().remove(token);
let wrapper = dispatcher.into_source_inner();
assert!(matches!(wrapper, TransientSource::None));
}
}