163 lines
4.4 KiB
Rust
163 lines
4.4 KiB
Rust
use anyhow::Result;
|
|
use log::trace;
|
|
use nautilus_common::on_drop::on_drop;
|
|
use std::any::type_name;
|
|
use std::fmt::Debug;
|
|
use std::ops::Deref;
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use std::sync::mpsc::{Receiver, Sender, channel};
|
|
use std::thread;
|
|
use std::thread::{Scope, sleep};
|
|
use std::time::{Duration, Instant};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct TaskHandle<Message, Data> {
|
|
pub sender: Sender<Message>,
|
|
data: Data,
|
|
}
|
|
|
|
impl<Message, Data> Deref for TaskHandle<Message, Data> {
|
|
type Target = Data;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.data
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub trait Task {
|
|
type Message;
|
|
type Data;
|
|
|
|
fn get_data(&self) -> Self::Data;
|
|
fn run(self, receiver: Receiver<Self::Message>, running: Arc<AtomicBool>);
|
|
}
|
|
|
|
pub trait CyclicTask {
|
|
type Message;
|
|
type Data;
|
|
|
|
fn get_data(&self) -> Self::Data;
|
|
fn step(&mut self, receiver: &Receiver<Self::Message>, step_time: Instant);
|
|
}
|
|
|
|
impl<F> CyclicTask for F
|
|
where
|
|
F: FnMut(),
|
|
{
|
|
type Message = ();
|
|
type Data = ();
|
|
|
|
fn get_data(&self) -> Self::Data {}
|
|
|
|
fn step(&mut self, _receiver: &Receiver<Self::Message>, _step_time: Instant) {
|
|
self();
|
|
}
|
|
}
|
|
|
|
pub struct Scheduler<'s, 'e>
|
|
where
|
|
'e: 's,
|
|
{
|
|
scope: &'s Scope<'s, 'e>,
|
|
running: Arc<AtomicBool>,
|
|
}
|
|
|
|
impl<'s> Scheduler<'s, '_> {
|
|
pub fn scope<'env, F, R>(running: Arc<AtomicBool>, f: F) -> R
|
|
where
|
|
F: FnOnce(Scheduler<'_, 'env>) -> R,
|
|
{
|
|
trace!("Scheduler::scope(running: {running:?}, f)");
|
|
thread::scope(|scope: &Scope| {
|
|
let running_result = running.clone();
|
|
// This will automatically set running to false when it drops
|
|
// This means that if the function returns any side branches
|
|
// checking running will shut down
|
|
let _shutdown_threads = on_drop(move || running.store(false, Ordering::Relaxed));
|
|
|
|
f(Scheduler {
|
|
scope,
|
|
running: running_result,
|
|
})
|
|
})
|
|
}
|
|
|
|
// pub fn inner_scope<'env, F, R>(&self, f: F) -> R
|
|
// where
|
|
// F: FnOnce(Scheduler<'_, 'env>) -> R,
|
|
// 's: 'env,
|
|
// {
|
|
// trace!("Scheduler::inner_scope(self)");
|
|
// thread::scope(|scope: &Scope<'_, 'env>| {
|
|
// f(Scheduler {
|
|
// scope,
|
|
// running: self.running.clone(),
|
|
// })
|
|
// })
|
|
// }
|
|
|
|
#[allow(dead_code)]
|
|
pub fn run<T>(
|
|
&self,
|
|
name: impl Into<String>,
|
|
task: T,
|
|
) -> Result<TaskHandle<T::Message, T::Data>>
|
|
where
|
|
T: Task + Send + Debug + 's,
|
|
T::Message: Send,
|
|
{
|
|
let name = name.into();
|
|
trace!(
|
|
"Scheduler::run<T={}>(name: {name}, task: {task:?})",
|
|
type_name::<T>()
|
|
);
|
|
let running = self.running.clone();
|
|
let (sender, receiver) = channel::<T::Message>();
|
|
let data = task.get_data();
|
|
let _ = thread::Builder::new()
|
|
.name(name.clone())
|
|
.spawn_scoped(self.scope, move || {
|
|
task.run(receiver, running);
|
|
})?;
|
|
Ok(TaskHandle { sender, data })
|
|
}
|
|
|
|
pub fn run_cyclic<T>(
|
|
&self,
|
|
name: impl Into<String>,
|
|
mut task: T,
|
|
frequency: u64,
|
|
) -> Result<TaskHandle<T::Message, T::Data>>
|
|
where
|
|
T: CyclicTask + Send + 's,
|
|
T::Message: Send,
|
|
{
|
|
let name = name.into();
|
|
trace!(
|
|
"Scheduler::run_cyclic<T={}>(name: {name}, task, frequency: {frequency})",
|
|
type_name::<T>()
|
|
);
|
|
let running = self.running.clone();
|
|
let (sender, receiver) = channel::<T::Message>();
|
|
let data = task.get_data();
|
|
let _ = thread::Builder::new()
|
|
.name(name.clone())
|
|
.spawn_scoped(self.scope, move || {
|
|
let period = Duration::from_nanos(1_000_000_000 / frequency);
|
|
let mut cycle_start_time = Instant::now();
|
|
while running.load(Ordering::Relaxed) {
|
|
task.step(&receiver, cycle_start_time);
|
|
|
|
cycle_start_time += period;
|
|
let sleep_duration = cycle_start_time - Instant::now();
|
|
if sleep_duration > Duration::ZERO {
|
|
sleep(sleep_duration);
|
|
}
|
|
}
|
|
})?;
|
|
Ok(TaskHandle { sender, data })
|
|
}
|
|
}
|