Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 821bc45a authored by Zach Johnson's avatar Zach Johnson Committed by Gerrit Code Review
Browse files

Merge changes I560edb20,Id8f90a59,If35e7bfc

* changes:
  rusty-gd: some tidying up in rootcanal_hal
  rusty-gd: Add cxx interop layer for the BT HIDL HAL
  eusty-gd: Add timerfd-based timing abstractions
parents 7594f8c7 54455340
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
rust_library {
    name: "libbt_common",
    crate_name: "bt_common",
    srcs: ["src/lib.rs"],
    edition: "2018",
    rustlibs: [
        "libtokio",
        "libnix",
    ],
    host_supported: true,
}

rust_test_host {
    name: "libbt_common_inline_tests",
    srcs: ["src/lib.rs"],
    test_suites: ["general-tests"],
    auto_gen_config: true,
    rustlibs: [
        "libtokio",
        "libnix",
    ],
}
+16 −0
Original line number Diff line number Diff line
/// Assertion check for X is is within Y of Z
#[macro_export]
macro_rules! assert_near {
    ($thing:expr, $expected:expr, $error:expr) => {
        match (&$thing, &$expected, &$error) {
            (thing_val, expected_val, error_val) => {
                if thing_val < &(expected_val - error_val) || thing_val > &(expected_val + error_val) {
                    panic!(
                        "assertion failed: {:?} is not within {:?} of {:?}",
                        &*thing_val, &*error_val, &*expected_val
                    )
                }
            }
        }
    };
}
+12 −0
Original line number Diff line number Diff line
//! Bluetooth common library

/// Provides waking timer abstractions
pub mod time;

#[macro_use]
mod ready;


#[cfg(test)]
#[macro_use]
mod asserts;
+10 −0
Original line number Diff line number Diff line
/// Simplifies polling futures
#[macro_export]
macro_rules! ready {
    ($e:expr $(,)?) => {
        match $e {
            std::task::Poll::Ready(t) => t,
            std::task::Poll::Pending => return std::task::Poll::Pending,
        }
    };
}
+147 −0
Original line number Diff line number Diff line
///! Waking timers for Bluetooth. Implemented using timerfd, but supposed to feel similar to
///Tokio's time
use crate::ready;

use nix::sys::time::TimeSpec;
use nix::sys::timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags};
use nix::unistd::close;
use std::future::Future;
use std::os::unix::io::AsRawFd;
use std::pin::Pin;
use std::task::{self, Poll};
use std::time::Duration;
use tokio::io::unix::AsyncFd;

/// Similar to tokio's sleep()
pub fn sleep(duration: Duration) -> Sleep {
    let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
    timer
        .set(
            Expiration::OneShot(TimeSpec::from(duration)),
            TimerSetTimeFlags::empty(),
        )
        .unwrap();

    Sleep {
        fd: AsyncFd::new(timer).unwrap(),
    }
}

/// Future returned by sleep()
pub struct Sleep {
    fd: AsyncFd<TimerFd>,
}

impl Future for Sleep {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        match ready!(self.fd.poll_read_ready(cx)) {
            Ok(_) => {
                // Will not block, since we have confirmed it is readable
                self.fd.get_ref().wait().unwrap();
                Poll::Ready(())
            }
            Err(e) => panic!("timer error: {}", e),
        }
    }
}

impl Drop for Sleep {
    fn drop(&mut self) {
        close(self.fd.as_raw_fd()).unwrap();
    }
}

/// Similar to tokio's interval, except the first tick does *not* complete immediately
pub fn interval(period: Duration) -> Interval {
    let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
    timer
        .set(
            Expiration::Interval(TimeSpec::from(period)),
            TimerSetTimeFlags::empty(),
        )
        .unwrap();

    Interval {
        fd: AsyncFd::new(timer).unwrap(),
    }
}

/// Future returned by interval()
pub struct Interval {
    fd: AsyncFd<TimerFd>,
}

impl Interval {
    /// Call this to get the future for the next tick of the interval
    pub async fn tick(&mut self) {
        drop(self.fd.readable().await.unwrap());
        // Will not block, since we have confirmed it is readable
        self.fd.get_ref().wait().unwrap();
    }
}

impl Drop for Interval {
    fn drop(&mut self) {
        close(self.fd.as_raw_fd()).unwrap();
    }
}

fn get_clock() -> ClockId {
    if cfg!(target_os = "android") {
        ClockId::CLOCK_BOOTTIME_ALARM
    } else {
        ClockId::CLOCK_BOOTTIME
    }
}

#[cfg(test)]
mod tests {
    use super::interval;
    use super::sleep;
    use crate::assert_near;
    use std::time::{Duration, Instant};

    #[test]
    fn sleep_schedule_and_then_drop() {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async {
            sleep(Duration::from_millis(200));
        });
    }

    #[test]
    fn sleep_simple_case() {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async {
            let timer = Instant::now();
            sleep(Duration::from_millis(10)).await;

            assert_near!(timer.elapsed().as_millis(), 10, 3);
        });
    }

    #[test]
    fn interval_schedule_and_then_drop() {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async {
            interval(Duration::from_millis(10));
        });
    }

    #[test]
    fn interval_simple_case() {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async {
            let timer = Instant::now();
            let mut interval = interval(Duration::from_millis(10));

            for n in 1..10 {
                interval.tick().await;
                println!("{}", n);
                assert_near!(timer.elapsed().as_millis(), 10 * n, 3);
            }
        });
    }
}
Loading