feat(backend) scheduler rewrite (deadlock for now)

This commit is contained in:
lumither committed 2025-03-05 13:52:07 -08:00
1 parent 0c6325d5f2
commit 5e8b1ff35d
8 files changed
+267 -139

No files matched your search

+48 -123
View File
@@ -1,172 +1,94 @@
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering}, Arc,
RwLock,
},
thread,
collections::{HashMap, HashSet},
sync::{Arc, RwLock},
};
use crate::scheduler::{
scheduler_error::{
SchedulerError, SchedulerError::IdAlreadyRunning, SchedulerError::IdNotFound,
},
sleeper::SkippableSleeper,
runner::Runner,
scheduler_error::{SchedulerError, SchedulerError::IdNotFound},
tasks::CronTask,
};
use tracing::log::error;
use uuid::Uuid;
mod runner;
pub mod scheduler_error;
mod sleeper;
mod task_func;
pub mod tasks;
struct TaskStatus {
sleeper: Option<Arc<SkippableSleeper>>,
is_alive: AtomicBool,
}
#[derive(Default)]
pub struct Scheduler {
tasks: Arc<RwLock<HashMap<Uuid, Arc<Box<dyn CronTask>>>>>,
running: Arc<RwLock<HashMap<Uuid, Arc<TaskStatus>>>>,
running: Arc<RwLock<HashSet<Uuid>>>,
parking_pool: Arc<RwLock<HashMap<Uuid, Arc<Box<dyn CronTask>>>>>,
runner: Runner,
}
impl Scheduler {
/// Start all tasks in the scheduler
pub fn start(&self) -> Result<(), SchedulerError> {
for (k, _v) in self.tasks.read()?.iter() {
self.run_non_blocking(k)?
}
Ok(())
self.runner.run_all()
}
/// Terminate all tasks in the scheduler
pub fn halt() {}
fn run_task(
running: Arc<RwLock<HashMap<Uuid, Arc<TaskStatus>>>>,
id: Uuid,
task: Arc<Box<dyn CronTask>>,
) -> Result<(), SchedulerError> {
if running.read()?.get(&id).is_some() {
return Err(IdAlreadyRunning);
} else {
running.write()?.insert(
id,
TaskStatus {
sleeper: None,
is_alive: AtomicBool::new(true),
}
.into(),
);
}
loop {
if let Some(next_execution) = task.get_next_execution() {
// guarantee is alive
let task_status = {
let running_reading_guard = running.read()?;
running_reading_guard.get(&id).cloned().clone()
};
if let Some(task_status) = task_status {
if !&task_status.is_alive.load(Ordering::SeqCst) {
{
let mut running_lock_guard = running.write()?;
running_lock_guard.remove(&id);
}
return Ok(());
} else {
let sleeper = Arc::from(SkippableSleeper::from(next_execution));
{
let mut running_lock_guard = running.write()?;
running_lock_guard.insert(
id,
TaskStatus {
sleeper: Some(sleeper.clone()),
is_alive: AtomicBool::new(true),
}
.into(),
);
}
sleeper.start();
task.call();
}
}
} else {
error!("unexpected error ");
break;
}
pub fn halt(&self) -> Result<(), SchedulerError> {
for id in self.running.read()?.iter() {
let func = &self.runner.terminate_remove(*id)?;
self.running.write()?.remove(id);
self.parking_pool.write()?.insert(*id, func.clone());
}
Ok(())
}
/// Run a task with `id`
pub fn run(&self, id: &Uuid) -> Result<(), SchedulerError> {
let id = id.to_owned();
if let Some(task) = self.tasks.read()?.get(&id) {
Self::run_task(self.running.clone(), id, task.clone())
} else {
Err(IdNotFound)
}
}
pub fn run_non_blocking(&self, id: &Uuid) -> Result<(), SchedulerError> {
let id = id.to_owned();
if let Some(task) = self.tasks.read()?.get(&id) {
let running_clone = self.running.clone();
let task_clone = task.clone();
thread::spawn(move || Self::run_task(running_clone, id, task_clone));
if self.running.read()?.contains(id) {
Ok(())
} else if self.parking_pool.read()?.contains_key(id) {
let func = self.parking_pool.write()?.remove(id).unwrap();
self.running.write()?.insert(*id);
self.runner.add_with_id(func, id)
} else {
Err(IdNotFound)
}
}
fn get_running(&self, id: &Uuid) -> Option<Arc<TaskStatus>> {
let ret = self.running.read().unwrap().get(id).cloned();
ret
}
/// Stop a task with `id`
pub fn stop(&self, id: &Uuid) -> Result<(), SchedulerError> {
match self.get_running(id) {
None => Err(IdNotFound),
Some(status) => {
status.is_alive.store(false, Ordering::SeqCst);
if let Some(s) = &status.sleeper {
s.skip();
} else {
return Err(IdAlreadyRunning);
}
Ok(())
}
dbg!("stop() called");
if self.running.read()?.contains(id) {
dbg!("b1");
let func = self.runner.terminate_remove(*id)?;
dbg!("b1a");
self.running.write()?.remove(id);
dbg!("b1b");
self.parking_pool.write()?.insert(*id, func.clone());
dbg!("b1c");
Ok(())
} else if self.parking_pool.read()?.get(id).is_some() {
dbg!("b2");
Ok(())
} else {
dbg!("b3");
Err(IdNotFound)
}
}
pub fn now(&self, id: &Uuid) -> Result<(), SchedulerError> {
match self.get_running(id) {
None => Err(IdNotFound),
Some(status) => {
if let Some(s) = &status.sleeper {
s.skip()
} else {
return Err(IdAlreadyRunning);
}
Ok(())
}
}
}
// pub fn now(&self, id: &Uuid) -> Result<(), SchedulerError> {
// }
pub fn insert(&self, task: Box<dyn CronTask>) -> Result<Uuid, SchedulerError> {
let id = Uuid::new_v4();
self.tasks.write()?.insert(id, Arc::new(task));
let id = self.runner.add(task.into())?;
self.running.write()?.insert(id);
Ok(id)
}
pub fn insert_list(&self, tasks: Vec<Box<dyn CronTask>>) -> Result<Vec<Uuid>, SchedulerError> {
tasks.into_iter().map(|t| self.insert(t)).collect()
let ids = self
.runner
.add_list(tasks.into_iter().map(|t| t.into()).collect())?;
self.running.write()?.extend(&ids);
Ok(ids)
}
pub fn new() -> Self {
@@ -187,11 +109,14 @@ mod tests {
let scheduler = Scheduler::default();
let task = BasicTask::new(|| println!("test print"), "* * * * * *")?;
let id = scheduler.insert(Box::new(task))?;
scheduler.start()?;
for i in 1..=2 {
println!("Loop {} start", i);
println!("Run {}", i);
scheduler.run_non_blocking(&id)?;
scheduler.run(&id)?;
println!("Sleep {}", i);
thread::sleep(Duration::from_secs(4));
println!("Sleep {} finished", i);
scheduler.stop(&id)?;
println!("Stop {}", i);
thread::sleep(Duration::from_secs(2));