embedded-time 0.12.1

Fully defined, inter-operable, ergonomic, and fast human-time units (both duration and rate types) with hardware timer abstraction and software timers.
Documentation
use crossbeam_utils::thread;
use embedded_time::{
    self as time, duration::*, fixed_point, fraction::Fraction, Clock as _, Instant,
};
use std::sync::atomic::{AtomicU64, Ordering};

static TICKS: AtomicU64 = AtomicU64::new(0);

#[derive(Debug)]
struct Clock;
impl time::Clock for Clock {
    type T = u64;
    const SCALING_FACTOR: Fraction = Fraction::new(1, 1_000);

    fn try_now(&self) -> Result<Instant<Self>, time::clock::Error> {
        Ok(Instant::new(TICKS.load(Ordering::SeqCst)))
    }
}

#[test]
fn oneshot_wait() {
    init_ticks();
    let clock = Clock;

    let timer = clock.new_timer(1_u32.seconds()).start().unwrap();

    thread::scope(|s| {
        let timer_handle = s.spawn(|_| timer.wait());

        add_to_ticks(1_u32.seconds());

        let result = timer_handle.join();

        assert!(result.is_ok());

        add_to_ticks(1_u32.seconds());

        let timer = result.unwrap().unwrap().start().unwrap();
        assert!(!timer.is_expired().unwrap());

        let timer_handle = s.spawn(|_| timer.wait());
        add_to_ticks(1_u32.seconds());

        assert!(timer_handle.join().is_ok());
    })
    .unwrap();
}

#[test]
fn periodic_wait() {
    init_ticks();
    let clock = Clock;

    let timer = clock
        .new_timer(1_u32.seconds())
        .into_periodic()
        .start()
        .unwrap();

    thread::scope(|s| {
        let timer_handle = s.spawn(|_| timer.wait());

        add_to_ticks(1_u32.seconds());

        let result = timer_handle.join();

        assert!(result.is_ok());

        let timer = result.unwrap();

        // WHEN blocking on a timer
        let timer_handle = s.spawn(|_| timer.unwrap().wait());

        add_to_ticks(1_u32.seconds());

        assert!(timer_handle.join().is_ok());
    })
    .unwrap();
}

#[test]
fn periodic_expiration() {
    init_ticks();
    let clock = Clock;

    let mut timer = clock
        .new_timer(1_u32.seconds())
        .into_periodic()
        .start()
        .unwrap();

    add_to_ticks(2_u32.seconds());

    assert!(timer.period_complete().unwrap());
    assert!(timer.period_complete().unwrap());
}

#[test]
fn read_timer() {
    init_ticks();
    let clock = Clock;

    let timer = clock.new_timer(2_u32.seconds()).start().unwrap();

    add_to_ticks(1_u32.milliseconds());

    assert_eq!(timer.elapsed(), Ok(0_u32.seconds()));
    assert_eq!(timer.remaining(), Ok(1_u32.seconds()));

    add_to_ticks(1_u32.seconds());

    assert_eq!(timer.elapsed(), Ok(1_u32.seconds()));
    assert_eq!(timer.remaining(), Ok(0_u32.seconds()));

    add_to_ticks(1_u32.seconds());

    assert_eq!(timer.elapsed(), Ok(2_u32.seconds()));
    assert_eq!(timer.remaining(), Ok(0_u32.seconds()));

    add_to_ticks(1_u32.seconds());

    assert_eq!(timer.elapsed(), Ok(3_u32.seconds()));
    assert_eq!(timer.remaining(), Ok(0_u32.seconds()));
}

fn init_ticks() {}

fn add_to_ticks<Dur: Duration>(duration: Dur)
where
    Dur: fixed_point::FixedPoint,
    u64: From<Dur::T>,
{
    let ticks = TICKS.load(Ordering::SeqCst);
    let ticks = ticks
        + duration
            .to_generic::<u64>(Clock::SCALING_FACTOR)
            .unwrap()
            .integer();
    TICKS.store(ticks, Ordering::SeqCst);
}