use std::{
cmp,
collections::{BinaryHeap, HashMap},
sync::{
Arc, Mutex,
atomic::{AtomicU64, Ordering},
},
time::{Duration, Instant},
};
use reifydb_core::Result;
use reifydb_engine::StandardEngine;
use reifydb_sub_api::{BoxedOnceTask, BoxedTask, Priority, TaskContext as CoreTaskContext, TaskHandle};
use crate::task::{InternalTaskContext, PoolTask, ScheduledTask};
pub(crate) struct SchedulableTaskAdapter {
task: BoxedTask,
engine: StandardEngine,
}
impl SchedulableTaskAdapter {
pub(crate) fn new(task: BoxedTask, engine: StandardEngine) -> Self {
Self {
task,
engine,
}
}
}
impl PoolTask for SchedulableTaskAdapter {
fn execute(&self, _ctx: &InternalTaskContext) -> Result<()> {
let core_ctx = CoreTaskContext::new(self.engine.clone());
self.task.execute(&core_ctx)
}
fn priority(&self) -> Priority {
self.task.priority()
}
fn name(&self) -> &str {
self.task.name()
}
}
pub(crate) struct OnceTaskAdapter {
task: Mutex<Option<BoxedOnceTask>>,
engine: StandardEngine,
}
impl OnceTaskAdapter {
pub(crate) fn new(task: BoxedOnceTask, engine: StandardEngine) -> Self {
Self {
task: Mutex::new(Some(task)),
engine,
}
}
}
impl PoolTask for OnceTaskAdapter {
fn execute(&self, _ctx: &InternalTaskContext) -> Result<()> {
let task = self.task.lock().unwrap().take();
if let Some(task) = task {
let core_ctx = CoreTaskContext::new(self.engine.clone());
task.execute_once(&core_ctx)
} else {
panic!("OnceTask already executed");
}
}
fn priority(&self) -> Priority {
self.task.lock().unwrap().as_ref().map(|t| t.priority()).unwrap_or(Priority::Normal)
}
fn name(&self) -> &str {
"once-task"
}
}
pub struct TaskScheduler {
next_handle: AtomicU64,
tasks: HashMap<TaskHandle, ScheduledTask>,
queue: BinaryHeap<ScheduledTaskRef>,
}
struct ScheduledTaskRef {
handle: TaskHandle,
next_run: Instant,
priority: Priority,
}
impl PartialEq for ScheduledTaskRef {
fn eq(&self, other: &Self) -> bool {
self.next_run == other.next_run && self.priority == other.priority
}
}
impl Eq for ScheduledTaskRef {}
impl PartialOrd for ScheduledTaskRef {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ScheduledTaskRef {
fn cmp(&self, other: &Self) -> cmp::Ordering {
match other.next_run.cmp(&self.next_run) {
cmp::Ordering::Equal => {
self.priority.cmp(&other.priority)
}
other => other,
}
}
}
impl TaskScheduler {
pub fn new() -> Self {
Self {
next_handle: AtomicU64::new(1),
tasks: HashMap::new(),
queue: BinaryHeap::new(),
}
}
pub fn set_next_handle(&self, handle_id: u64) {
self.next_handle.store(handle_id, Ordering::Relaxed);
}
pub fn schedule_every_internal(
&mut self,
task: Box<dyn PoolTask>,
interval: Duration,
priority: Priority,
) -> TaskHandle {
let handle = TaskHandle::from(self.next_handle.fetch_add(1, Ordering::Relaxed));
let next_run = Instant::now() + interval;
let scheduled = ScheduledTask::new(handle, task, next_run, Some(interval), priority);
self.queue.push(ScheduledTaskRef {
handle,
next_run,
priority,
});
self.tasks.insert(handle, scheduled);
handle
}
pub fn cancel(&mut self, handle: TaskHandle) {
self.tasks.remove(&handle);
}
pub fn get_ready_tasks(&mut self) -> Vec<Box<dyn PoolTask>> {
let now = Instant::now();
let mut ready = Vec::new();
while let Some(task_ref) = self.queue.peek() {
if task_ref.next_run > now {
break; }
let task_ref = self.queue.pop().unwrap();
if let Some(mut scheduled) = self.tasks.remove(&task_ref.handle) {
let shared_task = SharedTask::new(scheduled.task.clone());
ready.push(Box::new(shared_task) as Box<dyn PoolTask>);
if let Some(interval) = scheduled.interval {
scheduled.next_run = now + interval;
self.queue.push(ScheduledTaskRef {
handle: task_ref.handle,
next_run: scheduled.next_run,
priority: scheduled.priority,
});
self.tasks.insert(task_ref.handle, scheduled);
}
}
}
ready
}
pub fn next_run_time(&self) -> Option<Instant> {
self.queue.peek().map(|t| t.next_run)
}
pub fn task_count(&self) -> usize {
self.tasks.len()
}
}
struct SharedTask(Arc<dyn PoolTask>);
impl SharedTask {
fn new(task: Arc<dyn PoolTask>) -> Self {
Self(task)
}
}
impl PoolTask for SharedTask {
fn execute(&self, ctx: &InternalTaskContext) -> crate::Result<()> {
self.0.execute(ctx)
}
fn priority(&self) -> Priority {
self.0.priority()
}
fn name(&self) -> &str {
self.0.name()
}
fn can_retry(&self) -> bool {
self.0.can_retry()
}
fn max_retries(&self) -> usize {
self.0.max_retries()
}
}