Skip to content

Commit

Permalink
Merge pull request #25 from ERobsham/bugfix/respect_timeout_on_linux
Browse files Browse the repository at this point in the history
Respect poll timeout on linux
  • Loading branch information
windy1 authored Aug 9, 2023
2 parents 38466da + a6e399d commit df25803
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 7 deletions.
1 change: 1 addition & 0 deletions zeroconf/src/linux/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ impl AvahiBrowserContext {
}
}

#[allow(clippy::derivable_impls)]
impl Default for AvahiBrowserContext {
fn default() -> Self {
AvahiBrowserContext {
Expand Down
11 changes: 6 additions & 5 deletions zeroconf/src/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ pub struct AvahiEventLoop<'a> {
impl<'a> TEventLoop for AvahiEventLoop<'a> {
/// Polls for new events.
///
/// Internally calls `ManagedAvahiSimplePoll::iterate(0)`, the `timeout` parameter does not
/// currently do anything in the Avahi implementation.
fn poll(&self, _timeout: Duration) -> Result<()> {
self.poll.iterate(0);
Ok(())
/// Internally calls `ManagedAvahiSimplePoll::iterate(..)`.
/// In systems where the C implementation of `poll(.., timeout)`
/// does not respect the `timeout` parameter, the `timeout` passed
/// here will have no effect -- ie will return immediately.
fn poll(&self, timeout: Duration) -> Result<()> {
self.poll.iterate(timeout)
}
}
20 changes: 18 additions & 2 deletions zeroconf/src/linux/poll.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Rust friendly `AvahiSimplePoll` wrappers/helpers

use crate::error::Error;
use crate::Result;
use avahi_sys::{
avahi_simple_poll_free, avahi_simple_poll_iterate, avahi_simple_poll_loop,
avahi_simple_poll_new, AvahiSimplePoll,
};
use std::{convert::TryInto, time::Duration};

/// Wraps the `AvahiSimplePoll` type from the raw Avahi bindings.
///
Expand Down Expand Up @@ -38,8 +40,22 @@ impl ManagedAvahiSimplePoll {
/// Delegate function for [`avahi_simple_poll_iterate()`].
///
/// [`avahi_simple_poll_iterate()`]: https://avahi.org/doxygen/html/simple-watch_8h.html#ad5b7c9d3b7a6584d609241ee6f472a2e
pub fn iterate(&self, sleep_time: i32) {
unsafe { avahi_simple_poll_iterate(self.0, sleep_time) };
pub fn iterate(&self, timeout: Duration) -> Result<()> {
let sleep_time: i32 = timeout
.as_millis() // `avahi_simple_poll_iterate()` expects `sleep_time` in msecs.
.try_into() // `avahi_simple_poll_iterate()` expects `sleep_time` as an i32.
.unwrap_or(i32::MAX); // if converting to an i32 overflows, just use the largest number we can.

// Returns -1 on error, 0 on success and 1 if a quit request has been scheduled
match unsafe { avahi_simple_poll_iterate(self.0, sleep_time) } {
0 | 1 => Ok(()),
-1 => Err(Error::from(
"avahi_simple_poll_iterate(..) threw an error result",
)),
_ => Err(Error::from(
"avahi_simple_poll_iterate(..) returned an unknown result",
)),
}
}

pub(super) fn inner(&self) -> *mut AvahiSimplePoll {
Expand Down
85 changes: 85 additions & 0 deletions zeroconf/src/tests/event_loop_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::prelude::*;
use crate::{MdnsService, ServiceType, TxtRecord};
use std::time::{Duration, Instant};

const TEST_DURATION: Duration = Duration::from_secs(1);

const FAST_SPIN_TIMEOUT: Duration = Duration::from_secs(0);
const FAST_SPIN_MIN_ITERS: u32 = 10_000;

const LONG_POLL_TIMEOUT: Duration = Duration::from_secs(1);
const LONG_POLL_MAX_ITERS: u32 = 100;

#[test]
fn event_loop_spins_fast() {
super::setup();

static SERVICE_NAME: &str = "event_loop_test_service";
let mut service = MdnsService::new(ServiceType::new("http", "tcp").unwrap(), 8080);

let mut txt = TxtRecord::new();
txt.insert("foo", "bar").unwrap();

service.set_name(SERVICE_NAME);
service.set_txt_record(txt.clone());
service.set_registered_callback(Box::new(|_, _| {
debug!("Service published");
}));

let start = Instant::now();
let mut iterations = 0;
let event_loop = service.register().unwrap();
loop {
event_loop.poll(FAST_SPIN_TIMEOUT).unwrap();

if Instant::now().saturating_duration_since(start) >= TEST_DURATION {
break;
}
iterations += 1;
}

println!(
"service loop spun {} times in {} sec",
iterations,
TEST_DURATION.as_secs()
);

assert!(iterations > FAST_SPIN_MIN_ITERS);
}

#[test]
fn event_loop_long_polls() {
super::setup();

static SERVICE_NAME: &str = "event_loop_test_service";
let mut service = MdnsService::new(ServiceType::new("http", "tcp").unwrap(), 8080);

let mut txt = TxtRecord::new();
txt.insert("foo", "bar").unwrap();

service.set_name(SERVICE_NAME);
service.set_txt_record(txt.clone());
service.set_registered_callback(Box::new(|_, _| {
debug!("Service published");
}));

let start = Instant::now();
let mut iterations = 0;
let event_loop = service.register().unwrap();
loop {
event_loop.poll(LONG_POLL_TIMEOUT).unwrap();

if Instant::now().saturating_duration_since(start) >= TEST_DURATION {
break;
}
iterations += 1;
}

println!(
"service loop spun {} times in {} sec",
iterations,
TEST_DURATION.as_secs()
);

assert!(LONG_POLL_MAX_ITERS > iterations);
}
1 change: 1 addition & 0 deletions zeroconf/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ pub(crate) fn setup() {
INIT.call_once(env_logger::init);
}

mod event_loop_test;
mod service_test;
mod txt_record_test;

0 comments on commit df25803

Please sign in to comment.