ztimer 0.1.2

A block-based, non-circular double-linked list implementation for Rust.
Documentation
// Copyright 2024 Lorby Bi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::timer::TimerHandle;
use super::{ClockPtr, Interval, SyncUnsafeRcRefCell, SyncUnsafeWeakRefCell, Tick, Timer, TPL};
use anyhow::{Ok, Result as AnyResult};
use std::ops::Sub;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use std::time::{Duration, Instant};

use crate::NonBlockTickBridge;
use log::info;
use rblist::{BList, Node, Scale};

const MAX_COUNT_PER_SCHEDULE: u32 = 100;
const MAX_TIMERS_PER_TICK: u32 = 10000;
static mut CLOCK_ID: AtomicUsize = AtomicUsize::new(1);
pub(super) type Scheduler = BList<Option<SyncUnsafeRcRefCell<TPL>>>;
pub type MockClockPtr = Pin<&'static Mutex<MockClock>>;
#[derive(Debug)]
pub struct MockClock {
    me: Option<MockClockPtr>,
    id: usize,
    start: Tick,
    last_check: Tick,
    last: Tick,
    now: Tick,
    ts_start: Instant,
    ts_last: Instant,
    ts_now: Instant,
    precision: u32,
}

impl MockClock {
    pub fn init_distributed_clocks() {}
    pub fn new(_: Option<Box<NonBlockTickBridge>>) -> Option<Pin<&'static Mutex<Self>>> {
        let boxed = Box::new(Mutex::new(Self {
            me: None,
            id: unsafe { CLOCK_ID.fetch_add(1, Ordering::Relaxed) },
            start: 0.into(),
            last_check: 0.into(),
            last: 0.into(),
            now: 0.into(),
            ts_start: Instant::now(),
            ts_last: Instant::now(),
            ts_now: Instant::now(),
            precision: 500,
        }));

        let ptr: &'static Mutex<Self> = Box::leak(boxed);
        let me = Some(Pin::new(ptr));
        ptr.lock().unwrap().me = me;
        info!("The mocked clock is created.");

        me
    }

    pub fn new_timer<F>(
        clk: Pin<&'static Mutex<Self>>,
        duration: Duration,
        f: F,
        _name: String,
    ) -> AnyResult<Timer>
    where
        F: FnOnce() + Send + Sync + 'static,
    {
        info!(
            "Create a new timer with duration {:?} in mocked clock",
            duration
        );
        let interval: Interval = duration.into();
        let mut guard = clk.lock().unwrap(); // Crash in case of Poisoned lock.
        let clock = &mut *guard;
        let clock_ptr: *mut MockClock = clock;
        let tpl = TPL::new(clock_ptr, 100.into(), interval);
        let mut tpl = tpl.unsafe_try_borrow_mut().unwrap();

        tpl.new_timer(interval, Box::new(f), 10.into(), "test".into())
    }
    pub fn cancel_timer(_: Pin<&'static Mutex<Self>>, _t: &mut Timer) -> AnyResult<()> {
        info!("Cancel the timer in mocked clock");
        Ok(())
    }

    pub(super) fn remove_tpl(&mut self, interval: Interval) -> AnyResult<SyncUnsafeRcRefCell<TPL>> {
        info!("Remove the timer production line in mocked clock");
        Ok(TPL::new(self as *mut Self, 100.into(), interval))
    }
    pub(super) fn this(&self) -> ClockPtr {
        let x = self.me.unwrap();
        assert!(self.me.is_some()); //ClockPtr is copied
        x
    }
    pub(super) fn peg_timer_canceled(&mut self) {}
    pub(super) fn id(&self) -> usize {
        self.id
    }
    pub fn on_tick(clk: Pin<&'static Mutex<Self>>, tick: Tick) -> AnyResult<()> {
        let mut guard = clk.lock().unwrap(); // Crash in case of Poisoned lock.
        let clock = &mut *guard;

        if clock.start.0 == 0 {
            clock.start = tick;
            clock.ts_start = Instant::now();
            clock.last = tick;
            clock.ts_last = Instant::now();
            clock.now = tick;
            clock.ts_now = Instant::now();
            clock.last_check = tick;
        } else {
            clock.now = tick;
            assert!(clock.now.since(clock.last) == 1.into());
            clock.last = clock.now;
            if clock.now.since(clock.last_check) == 1000.into() {
                clock.ts_now = Instant::now();
                let delta = clock.ts_now.sub(clock.ts_last);

                info!(
                    "Clock[{}]:Time per 1000 ticks: {:?}, now:{:?}, start at:{:?}",
                    clock.id, delta, clock.now, clock.start
                );
                clock.ts_last = clock.ts_now;
                clock.last_check = clock.now;
                clock.precision = ((clock.precision + delta.as_millis() as u32) / 2) as u32;
            }
        }
        Ok(())
    }
    pub fn precision(clk: Pin<&'static Mutex<Self>>) -> u32 {
        let mut guard = clk.lock().unwrap(); // Crash in case of Poisoned lock.
        let clock = &mut *guard;
        clock.precision
    }
    pub fn clock_len(_clk: Pin<&'static Mutex<Self>>) -> isize {
        0
    }
    pub fn now(clk: Pin<&'static Mutex<Self>>) -> Tick {
        let guard = clk.lock().unwrap(); // Crash in case of Poisoned lock.
        let clock = &*guard;
        clock.now
    }
}

// Conductor handling
impl MockClock {
    pub fn schedule_tpl(
        &mut self,
        _: Tick,
        _: SyncUnsafeRcRefCell<TPL>,
    ) -> AnyResult<(SyncUnsafeWeakRefCell<Scheduler>, Node)> {
        info!("Schedule the timer production line in mocked clock");
        Ok((
            SyncUnsafeRcRefCell::unsafe_downgrade(&SyncUnsafeRcRefCell::new(BList::new(Scale::Medium))),
            Node::default(),
        ))
    }
    pub fn deschedule_tpl(
        &mut self,
        _: Tick,
        _: SyncUnsafeWeakRefCell<Scheduler>,
        _: Node,
    ) -> AnyResult<()> {
        info!("Deschedule the timer production line in mocked clock");
        Ok(())
    }

    // Return:(inspected, expired entries, all_done?)
    fn schedule(&mut self) -> (u32, BList<TimerHandle>, bool) {
        info!("Schedule the timer in mocked clock");
        (0, BList::new(Scale::Tiny), 0 > 0)
    }
}