feat(sched): ✨ proper task handlers
This commit is contained in:
parent
0f682a0600
commit
5b8a9faaba
@ -3,6 +3,8 @@ use lool::sched::{recur, ruleset, Scheduler};
|
|||||||
fn my_action() {
|
fn my_action() {
|
||||||
let now = chrono::Local::now();
|
let now = chrono::Local::now();
|
||||||
println!("I'm running at {}", now.format("%Y-%m-%d %H:%M:%S"));
|
println!("I'm running at {}", now.format("%Y-%m-%d %H:%M:%S"));
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(15));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -13,18 +15,15 @@ fn main() {
|
|||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
{
|
let is_running = handler.is_running();
|
||||||
let task = handler.task.lock().unwrap();
|
let last_run = handler.get_last_run();
|
||||||
|
let next_run = handler.get_next_run();
|
||||||
let is_running = task.is_running();
|
let name = handler.name();
|
||||||
let last_run = task.get_last_run();
|
|
||||||
let name = task.name();
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"task {} |--> is running: {}, last run: {:?}",
|
"task {name} |--> running: {is_running}, last: {last_run:?}, next: {next_run:?}"
|
||||||
name, is_running, last_run
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering},
|
||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,12 +21,14 @@ type Action = Box<dyn FnMut() + Send + Sync + 'static>;
|
|||||||
/// status of the task.
|
/// status of the task.
|
||||||
pub struct ScheduledTask {
|
pub struct ScheduledTask {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
index: usize,
|
index: Arc<AtomicUsize>,
|
||||||
name: String,
|
name: String,
|
||||||
action: Action,
|
action: Action,
|
||||||
rules: Vec<SchedulingRule>,
|
rules: Arc<Vec<SchedulingRule>>,
|
||||||
is_running: AtomicBool,
|
is_running: Arc<AtomicBool>,
|
||||||
last_run: Arc<Mutex<Option<DateTime<Local>>>>, // TODO: remaining limits
|
is_stopped: Arc<AtomicBool>,
|
||||||
|
is_removed: Arc<AtomicBool>,
|
||||||
|
last_run: Arc<AtomicPtr<DateTime<Local>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScheduledTask {
|
impl ScheduledTask {
|
||||||
@ -35,17 +37,16 @@ impl ScheduledTask {
|
|||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_last_run(&self) -> Option<DateTime<Local>> {
|
fn make_handler(&self) -> TaskHandler {
|
||||||
let last_run_lock = self.last_run.lock().unwrap();
|
TaskHandler {
|
||||||
last_run_lock.as_ref().cloned()
|
index: self.index.clone(),
|
||||||
|
name: self.name.clone(),
|
||||||
|
rules: self.rules.clone(),
|
||||||
|
is_running: self.is_running.clone(),
|
||||||
|
is_stopped: self.is_stopped.clone(),
|
||||||
|
is_removed: self.is_removed.clone(),
|
||||||
|
last_run: self.last_run.clone(),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_running(&self) -> bool {
|
|
||||||
self.is_running.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,19 +114,26 @@ impl Scheduler {
|
|||||||
let index = self.tasks.len();
|
let index = self.tasks.len();
|
||||||
|
|
||||||
let task = Arc::new(Mutex::new(ScheduledTask {
|
let task = Arc::new(Mutex::new(ScheduledTask {
|
||||||
index,
|
index: Arc::new(AtomicUsize::new(index)),
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
action: Box::new(action),
|
action: Box::new(action),
|
||||||
rules,
|
rules: Arc::new(rules),
|
||||||
is_running: AtomicBool::new(false),
|
is_running: Arc::new(AtomicBool::new(false)),
|
||||||
last_run: Arc::new(Mutex::new(None)),
|
is_stopped: Arc::new(AtomicBool::new(false)),
|
||||||
|
is_removed: Arc::new(AtomicBool::new(false)),
|
||||||
|
last_run: Arc::new(AtomicPtr::new(std::ptr::null_mut())),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
self.tasks.push(task.clone());
|
self.tasks.push(task.clone());
|
||||||
|
|
||||||
run_in_pool(task.clone(), &self.pool);
|
run_in_pool(task.clone(), &self.pool);
|
||||||
|
|
||||||
TaskHandler { task: task.clone() }
|
let handler: TaskHandler = {
|
||||||
|
let task = task.lock().unwrap();
|
||||||
|
task.make_handler()
|
||||||
|
};
|
||||||
|
|
||||||
|
handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,14 +142,77 @@ impl Scheduler {
|
|||||||
/// returned by the `Scheduler::schedule` method,
|
/// returned by the `Scheduler::schedule` method,
|
||||||
/// this struct can be used to check and control
|
/// this struct can be used to check and control
|
||||||
/// the status of the task.
|
/// the status of the task.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct TaskHandler {
|
pub struct TaskHandler {
|
||||||
// HACK: holding the task in the TaskHandler is a temporal hack
|
name: String,
|
||||||
// TaskHandler should hold Atomic references to the important parts of the task
|
index: Arc<AtomicUsize>,
|
||||||
// instead. e.g. is_running, last_run, etc.
|
rules: Arc<Vec<SchedulingRule>>,
|
||||||
// the problem is with last_run, as its a DateTime, and not a primitive type
|
is_running: Arc<AtomicBool>,
|
||||||
// we could get around this by instead of holding the DateTime, holding the i64
|
is_stopped: Arc<AtomicBool>,
|
||||||
// value (unix timestamp) and then converting it to a DateTime when needed.
|
is_removed: Arc<AtomicBool>,
|
||||||
pub task: Arc<Mutex<ScheduledTask>>,
|
last_run: Arc<AtomicPtr<DateTime<Local>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskHandler {
|
||||||
|
/// 🧉 » last run date
|
||||||
|
///
|
||||||
|
/// returns a `DateTime<Local>` representing the last time the task was run or None if the task
|
||||||
|
/// has never been run before.
|
||||||
|
pub fn get_last_run(&self) -> Option<DateTime<Local>> {
|
||||||
|
let last_run = self.last_run.load(Ordering::Relaxed);
|
||||||
|
if last_run.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(unsafe { *last_run })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » next run date
|
||||||
|
///
|
||||||
|
/// returns a `DateTime<Local>` representing the next time the task is scheduled to run
|
||||||
|
pub fn get_next_run(&self) -> Option<DateTime<Local>> {
|
||||||
|
get_next_run_time(&self.rules, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » is running?
|
||||||
|
///
|
||||||
|
/// returns a `bool` indicating if the task is currently running in this moment
|
||||||
|
pub fn is_running(&self) -> bool {
|
||||||
|
self.is_running.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » name
|
||||||
|
///
|
||||||
|
/// returns the name of the task
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » is active?
|
||||||
|
///
|
||||||
|
/// returns a `bool` indicating if the task is active
|
||||||
|
pub fn is_active(&self) -> bool {
|
||||||
|
!self.is_stopped.load(Ordering::Relaxed) && !self.is_removed.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » is stopped?
|
||||||
|
///
|
||||||
|
/// returns a `bool` indicating if the task has been stopped
|
||||||
|
///
|
||||||
|
/// a task being stopped means that it has been paused, but not removed from the scheduler.
|
||||||
|
/// So, although it's not running, it's still in the scheduler and can be resumed.
|
||||||
|
pub fn is_stopped(&self) -> bool {
|
||||||
|
self.is_stopped.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » is removed?
|
||||||
|
///
|
||||||
|
/// returns a `bool` indicating if the task has been removed
|
||||||
|
///
|
||||||
|
/// once a task is removed, it's no longer in the scheduler and can't be resumed.
|
||||||
|
pub fn is_removed(&self) -> bool {
|
||||||
|
self.is_removed.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **main function to run the task in the thread pool**
|
/// **main function to run the task in the thread pool**
|
||||||
@ -176,11 +247,16 @@ fn run_in_pool(task_mutex: Arc<Mutex<ScheduledTask>>, pool: &ThreadPool) {
|
|||||||
|
|
||||||
let mut task = task_mutex.lock().unwrap();
|
let mut task = task_mutex.lock().unwrap();
|
||||||
|
|
||||||
task.last_run = Arc::new(Mutex::new(Some(run_date)));
|
let run_date_box = Box::new(run_date);
|
||||||
|
let run_date_raw = Box::into_raw(run_date_box);
|
||||||
|
task.last_run.store(run_date_raw, Ordering::Relaxed);
|
||||||
|
|
||||||
task.is_running.store(true, Ordering::SeqCst);
|
task.is_running.store(true, Ordering::SeqCst);
|
||||||
task.run();
|
task.run();
|
||||||
task.is_running.store(false, Ordering::SeqCst);
|
task.is_running.store(false, Ordering::SeqCst);
|
||||||
maybe_next_run = get_next_run_time(&task.rules, Some(run_date));
|
|
||||||
|
let run_date_box = unsafe { Box::from_raw(task.last_run.load(Ordering::Relaxed)) };
|
||||||
|
maybe_next_run = get_next_run_time(&task.rules, Some(*run_date_box));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user