use std::sync::atomic::{self, AtomicBool};
use std::sync::Arc;
use std::time::{Duration, Instant};
pub struct SchedulerHandle(Arc<AtomicBool>);
impl Drop for SchedulerHandle {
fn drop(&mut self) {
self.cancel()
}
}
impl SchedulerHandle {
pub fn cancel(&self) {
self.0.store(true, atomic::Ordering::SeqCst);
}
}
enum JobType {
Once(Box<dyn FnMut() + Send + 'static>),
Recurring { f: Box<dyn FnMut() + Send + 'static>, rate: Duration },
}
struct Job {
type_: JobType,
time: Instant,
}
#[derive(Default)]
pub struct Scheduler {
job: Option<Job>,
cancelled: Arc<AtomicBool>,
}
impl Scheduler {
pub fn handle(&self) -> SchedulerHandle {
SchedulerHandle(self.cancelled.clone())
}
pub fn once<F>(f: F) -> Self
where
F: FnMut() + Send + 'static,
{
let job = Job { type_: JobType::Once(Box::new(f)), time: Instant::now() };
Self { job: Some(job), cancelled: Arc::new(AtomicBool::new(false)) }
}
pub fn delayed_once<F>(delay: Duration, f: F) -> Self
where
F: FnMut() + Send + 'static,
{
let job = Job { type_: JobType::Once(Box::new(f)), time: Instant::now() + delay };
Self { job: Some(job), cancelled: Arc::new(AtomicBool::new(false)) }
}
pub fn recurring<F>(rate: Duration, f: F) -> Self
where
F: FnMut() + Send + 'static,
{
let job = Job { type_: JobType::Recurring { f: Box::new(f), rate }, time: Instant::now() };
Self { job: Some(job), cancelled: Arc::new(AtomicBool::new(false)) }
}
pub fn delayed_recurring<F>(delay: Duration, rate: Duration, f: F) -> Self
where
F: FnMut() + Send + 'static,
{
let job = Job {
type_: JobType::Recurring { f: Box::new(f), rate },
time: Instant::now() + delay,
};
Self { job: Some(job), cancelled: Arc::new(AtomicBool::new(false)) }
}
pub fn start(self) -> SchedulerHandle {
let handle = self.handle();
std::thread::spawn(move || self.run());
handle
}
fn run(mut self) {
while let Some(job) = self.get_job() {
self.run_job(job);
}
}
fn get_job(&mut self) -> Option<Job> {
loop {
if self.cancelled.load(atomic::Ordering::SeqCst) {
return None;
}
let now = Instant::now();
match self.job.as_ref() {
None => {
break;
}
Some(j) if j.time <= now => {
break;
}
Some(j) => {
std::thread::sleep(j.time - now);
}
};
}
self.job.take()
}
fn run_job(&mut self, job: Job) {
match job.type_ {
JobType::Once(mut f) => {
f();
}
JobType::Recurring { mut f, rate } => {
f();
let new_job = Job { type_: JobType::Recurring { f, rate }, time: job.time + rate };
self.job = Some(new_job);
}
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}