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

Commit 7cb36a17 authored by Zach Johnson's avatar Zach Johnson
Browse files

rusty-gd: no more sleep

since it's backed by a fd, a structure that encourages reuse is a good
thing.

integrate this into HCI, for command timeouts

Bug: 171749953
Tag: #gd-refactor
Test: gd/cert/run --rhost SimpleHalTest
Change-Id: I3094c7a666281cebdfdeee05246ac1ad557988c5
parent 2b570b47
Loading
Loading
Loading
Loading
+41 −44
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 {
/// A single shot Alarm
pub struct Alarm {
    fd: AsyncFd<TimerFd>,
}

impl Alarm {
    /// Construct a new alarm
    pub fn new() -> Self {
        let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
    timer
        Self {
            fd: AsyncFd::new(timer).unwrap(),
        }
    }

    /// Reset the alarm to duration, starting from now
    pub fn reset(&mut self, duration: Duration) {
        self.fd.get_ref()
            .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>,
    /// Stop the alarm if it is currently started
    pub fn cancel(&mut self) {
        self.reset(Duration::from_millis(0));
    }

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(_) => {
    /// Completes when the alarm has expired
    pub async fn expired(&mut self) {
        drop(self.fd.readable().await.unwrap());
        // 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 Default for Alarm {
    fn default() -> Self {
        Alarm::new()
    }
}

impl Drop for Sleep {
impl Drop for Alarm {
    fn drop(&mut self) {
        close(self.fd.as_raw_fd()).unwrap();
    }
@@ -99,24 +102,18 @@ fn get_clock() -> ClockId {
#[cfg(test)]
mod tests {
    use super::interval;
    use super::sleep;
    use super::Alarm;
    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() {
    fn alarm_simple_case() {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async {
            let timer = Instant::now();
            sleep(Duration::from_millis(10)).await;
            let mut alarm = Alarm::new();
            alarm.reset(Duration::from_millis(10));
            alarm.expired().await;

            assert_near!(timer.elapsed().as_millis(), 10, 3);
        });
+7 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ use tokio::runtime::Runtime;
use tokio::select;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::{oneshot, Mutex};
use bt_common::time::Alarm;
use std::time::Duration;

module! {
    hci_module,
@@ -162,11 +164,13 @@ async fn dispatch(
    mut cmd_rx: Receiver<Command>,
) {
    let mut pending_cmd: Option<PendingCommand> = None;
    let mut hci_timeout = Alarm::new();
    loop {
        select! {
            Some(evt) = consume(&evt_rx) => {
                match evt.specialize() {
                    CommandStatus(evt) => {
                        hci_timeout.cancel();
                        let this_opcode = *evt.get_command_op_code();
                        match pending_cmd.take() {
                            Some(PendingCommand{opcode, fut}) if opcode == this_opcode  => fut.send(evt.into()).unwrap(),
@@ -175,6 +179,7 @@ async fn dispatch(
                        }
                    },
                    CommandComplete(evt) => {
                        hci_timeout.cancel();
                        let this_opcode = *evt.get_command_op_code();
                        match pending_cmd.take() {
                            Some(PendingCommand{opcode, fut}) if opcode == this_opcode  => fut.send(evt.into()).unwrap(),
@@ -207,7 +212,9 @@ async fn dispatch(
                    fut: cmd.fut,
                });
                cmd_tx.send(cmd.cmd).await.unwrap();
                hci_timeout.reset(Duration::from_secs(2));
            },
            _ = hci_timeout.expired() => panic!("Timed out waiting for {:?}", pending_cmd.unwrap().opcode),
            else => break,
        }
    }