use core::time::Duration;
use alloc::boxed::Box;
use crate::time::AsyncSleeper;
use crate::time::Instant;
use crate::time::Sleeper;
#[cfg(all(feature = "std", not(feature = "spin_sleep")))]
use crate::time::StdSleeper;
#[cfg(all(feature = "std", feature = "spin_sleep"))]
use crate::time::SpinSleeper;
#[cfg(all(feature = "std", not(feature = "spin_sleep")))]
pub type Scheduler = CustomScheduler<std::time::Instant, StdSleeper>;
#[cfg(all(feature = "std", feature = "spin_sleep"))]
pub type Scheduler = CustomScheduler<std::time::Instant, SpinSleeper>;
#[derive(Debug, Copy, Clone, Hash)]
pub struct CustomScheduler<INSTANT, SLEEPER> {
target_delta: Duration,
last_loop_end: INSTANT,
sleeper: SLEEPER,
}
impl<INSTANT, SLEEPER> Default for CustomScheduler<INSTANT, SLEEPER>
where
INSTANT: Instant,
SLEEPER: Sleeper + Default,
{
fn default() -> Self {
CustomScheduler::new(60.0)
}
}
impl<INSTANT, SLEEPER> CustomScheduler<INSTANT, SLEEPER>
where
INSTANT: Instant,
SLEEPER: Sleeper,
{
pub fn new(target_hz: f32) -> Self
where
SLEEPER: Default,
{
Self::with_sleeper(target_hz, SLEEPER::default())
}
pub fn with_sleeper(target_hz: f32, sleeper: SLEEPER) -> Self {
let target_delta = Duration::from_secs_f32(target_hz.recip());
CustomScheduler {
target_delta,
last_loop_end: INSTANT::now(),
sleeper,
}
}
pub fn set_hz(&mut self, new_target_hz: f32) {
self.target_delta = Duration::from_secs_f32(new_target_hz.recip())
}
pub fn hz(&self) -> f32 {
self.target_delta.as_secs_f32().recip()
}
pub fn loop_forever(&mut self, mut task: impl FnMut()) -> ! {
loop {
task();
self.sleep_until_next_frame();
}
}
pub fn loop_while_true(&mut self, mut task: impl FnMut() -> bool) {
loop {
if task() {
break;
}
self.sleep_until_next_frame();
}
}
pub fn loop_until_err<T>(
&mut self,
mut task: impl FnMut() -> Result<T, Box<dyn core::error::Error>>,
) -> Box<dyn core::error::Error> {
loop {
match task() {
Ok(_) => self.sleep_until_next_frame(),
Err(e) => return e,
}
}
}
pub fn sleep_until_next_frame(&mut self) {
let elapsed = self.last_loop_end.elapsed();
if elapsed < self.target_delta {
self.sleeper.sleep(self.target_delta - elapsed);
self.last_loop_end += self.target_delta;
} else {
self.last_loop_end = INSTANT::now();
}
}
}
#[derive(Debug, Copy, Clone, Hash)]
pub struct AsyncCustomScheduler<INSTANT, SLEEPER> {
target_delta: Duration,
last_loop_end: INSTANT,
sleeper: SLEEPER,
}
impl<INSTANT, SLEEPER> Default for AsyncCustomScheduler<INSTANT, SLEEPER>
where
INSTANT: Instant,
SLEEPER: AsyncSleeper + Default,
{
fn default() -> Self {
Self::new(60.0)
}
}
impl<INSTANT, SLEEPER> AsyncCustomScheduler<INSTANT, SLEEPER>
where
INSTANT: Instant,
SLEEPER: AsyncSleeper,
{
pub fn new(target_hz: f32) -> Self
where
SLEEPER: Default,
{
Self::with_sleeper(target_hz, SLEEPER::default())
}
pub fn with_sleeper(target_hz: f32, sleeper: SLEEPER) -> Self {
let target_delta = Duration::from_secs_f32(target_hz.recip());
Self {
target_delta,
last_loop_end: INSTANT::now(),
sleeper,
}
}
pub fn set_hz(&mut self, new_target_hz: f32) {
self.target_delta = Duration::from_secs_f32(new_target_hz.recip())
}
pub fn hz(&self) -> f32 {
self.target_delta.as_secs_f32().recip()
}
pub async fn loop_forever(&mut self, mut task: impl FnMut()) -> ! {
loop {
task();
self.sleep_until_next_frame().await;
}
}
pub async fn loop_while_true(&mut self, mut task: impl FnMut() -> bool) {
loop {
if task() {
break;
}
self.sleep_until_next_frame().await;
}
}
pub async fn loop_until_err<T>(
&mut self,
mut task: impl FnMut() -> Result<T, Box<dyn core::error::Error>>,
) -> Box<dyn core::error::Error> {
loop {
match task() {
Ok(_) => self.sleep_until_next_frame().await,
Err(e) => return e,
}
}
}
pub async fn sleep_until_next_frame(&mut self) {
let elapsed = self.last_loop_end.elapsed();
if elapsed < self.target_delta {
self.sleeper.sleep(self.target_delta - elapsed).await;
self.last_loop_end += self.target_delta;
} else {
self.last_loop_end = INSTANT::now();
}
}
}