use alloc::boxed::Box;
use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use core::task::Poll;
use futures::task::AtomicWaker;
use veecle_freertos_integration::{
Duration as FreeRtosDuration, TickType_t, Timer as FreeRtosTimer,
};
use veecle_osal_api::Error;
pub use veecle_osal_api::time::{
Duration, Exceeded, Instant, Interval, SystemTime, SystemTimeError, SystemTimeSync,
TimeAbstraction,
};
use crate::error::into_veecle_os_error;
#[derive(Debug)]
pub struct Time;
impl TimeAbstraction for Time {
fn now() -> Instant {
Instant::MIN
+ Duration::from_millis(
veecle_freertos_integration::scheduler::get_tick_count_duration().ms() as u64,
)
}
async fn sleep(duration: Duration) -> Result<(), Error> {
let duration = FreeRtosDuration::from_ms(duration.as_millis() as TickType_t);
if duration == FreeRtosDuration::zero() {
return Ok(());
}
#[derive(Default)]
struct Context {
waker: AtomicWaker,
done: AtomicBool,
}
let context = Arc::new(Context::default());
let callback = Box::new({
let context = context.clone();
move |_| {
context.done.store(true, Ordering::Relaxed);
context.waker.wake();
}
});
let timer =
FreeRtosTimer::periodic(None, duration, callback).map_err(into_veecle_os_error)?;
timer.handle().start().map_err(into_veecle_os_error)?;
core::future::poll_fn(move |cx| {
context.waker.register(cx.waker());
if context.done.load(Ordering::Relaxed) {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
Ok(())
}
async fn sleep_until(deadline: Instant) -> Result<(), Error> {
Self::sleep(
deadline
.duration_since(Self::now())
.unwrap_or(Duration::ZERO),
)
.await
}
fn interval(period: Duration) -> impl Interval
where
Self: Sized,
{
struct Context {
waker: AtomicWaker,
count: AtomicUsize,
}
struct IntervalInternal<F>
where
F: Fn(veecle_freertos_integration::TimerHandle) + Send + 'static,
{
_timer: Option<FreeRtosTimer<F>>,
error: Option<veecle_freertos_integration::FreeRtosError>,
context: Arc<Context>,
}
impl<F> Interval for IntervalInternal<F>
where
F: Fn(veecle_freertos_integration::TimerHandle) + Send + 'static,
{
async fn tick(&mut self) -> Result<(), Error> {
if let Some(error) = self.error.take() {
return Err(into_veecle_os_error(error));
}
core::future::poll_fn(|cx| {
self.context.waker.register(cx.waker());
if self.context.count.load(Ordering::Relaxed) != 0 {
self.context.count.fetch_sub(1, Ordering::Relaxed);
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
})
.await
}
}
let period = FreeRtosDuration::from_ms(period.as_millis() as TickType_t);
let context = Arc::new(Context {
waker: AtomicWaker::new(),
count: AtomicUsize::new(1),
});
let callback = Box::new({
let context = context.clone();
move |_| {
context.count.fetch_add(1, Ordering::Relaxed);
context.waker.wake();
}
});
let (mut error, timer) = match FreeRtosTimer::periodic(None, period, callback) {
Ok(timer) => (None, Some(timer)),
Err(error) => (Some(error), None),
};
if let Some(timer) = &timer {
error = timer.handle().start().err();
}
IntervalInternal {
_timer: timer,
error,
context,
}
}
}
static SYSTEM_TIME_OFFSET_SECONDS: AtomicU32 = AtomicU32::new(0);
impl SystemTime for Time {
fn duration_since_epoch() -> Result<Duration, SystemTimeError> {
let offset = SYSTEM_TIME_OFFSET_SECONDS.load(Ordering::Relaxed);
if offset == 0 {
Err(SystemTimeError::Unsynchronized)
} else {
let duration_since_start = Self::now()
.duration_since(Instant::MIN)
.expect("now can't be less than min time");
Ok(duration_since_start + Duration::from_secs(offset as u64))
}
}
}
impl SystemTimeSync for Time {
fn set_system_time(duration_since_epoch: Duration) -> Result<(), SystemTimeError> {
let duration_since_start = Duration::from_millis(
veecle_freertos_integration::scheduler::get_tick_count_duration().ms() as u64,
);
if duration_since_epoch < duration_since_start {
Err(SystemTimeError::EpochIsLaterThanStartTime)
} else {
SYSTEM_TIME_OFFSET_SECONDS.store(
(duration_since_epoch - duration_since_start).as_secs() as u32,
Ordering::Relaxed,
);
Ok(())
}
}
}