use arc_gc::gc::GC;
use std::{collections::VecDeque, sync::Arc};
use crate::{
lambda::runnable::{Runnable, RuntimeError, StepResult},
types::{
async_handle::OnionAsyncHandle,
object::{GCArcStorage, OnionObjectCell, OnionObjectExt},
},
unwrap_step_result,
};
const NUM_PRIORITY_LEVELS: usize = 3;
#[inline(always)]
fn generate_sched_step(n: usize) -> u64 {
(1u64 << (n + 1)) - 1
}
pub struct Task {
runnable: Box<dyn Runnable>,
task_handler: (Arc<OnionAsyncHandle>, GCArcStorage),
priority: usize, }
impl Task {
pub fn new(
runnable: Box<dyn Runnable>,
task_handler: (Arc<OnionAsyncHandle>, GCArcStorage),
priority: usize,
) -> Self {
Self {
runnable,
task_handler,
priority,
}
}
}
pub struct AsyncScheduler {
queue: VecDeque<Task>,
main_task_handler: (Arc<OnionAsyncHandle>, GCArcStorage), step: u64, }
impl AsyncScheduler {
pub fn new(main_task: Task) -> Self {
let mut queue = VecDeque::new();
let main_task_handler = main_task.task_handler.clone();
queue.push_back(main_task);
AsyncScheduler {
queue,
main_task_handler,
step: 0,
}
}
}
impl Runnable for AsyncScheduler {
fn step(&mut self, gc: &mut GC<OnionObjectCell>) -> StepResult {
let len = self.queue.len();
if len == 0 {
return StepResult::Return(
unwrap_step_result!(self.main_task_handler.0.value_of()).into(),
);
}
let mut i = 0;
self.step += 1;
while i < len {
if let Some(mut task) = self.queue.pop_front() {
if self.step % generate_sched_step(task.priority) == 0 {
let step_result = task.runnable.step(gc);
match step_result {
StepResult::Continue => {
task.priority = 0; self.queue.push_back(task);
}
StepResult::Return(ref result) => {
unwrap_step_result!(task.task_handler.0.set_result(result.weak()));
}
StepResult::Error(RuntimeError::Pending) => {
task.priority = std::cmp::min(task.priority + 1, NUM_PRIORITY_LEVELS);
self.queue.push_back(task);
}
e @ StepResult::Error(_) => return e,
StepResult::NewRunnable(_) => {
return StepResult::Error(RuntimeError::DetailedError(
"AsyncScheduler does not support NewRunnable"
.to_string()
.into(),
));
}
StepResult::ReplaceRunnable(_) => {
return StepResult::Error(RuntimeError::DetailedError(
"AsyncScheduler does not support ReplaceRunnable"
.to_string()
.into(),
));
}
StepResult::SpawnRunnable(new_task) => {
self.queue.push_back(*new_task);
self.queue.push_back(task);
}
}
} else {
self.queue.push_back(task);
}
}
i += 1;
}
StepResult::Continue
}
fn receive(
&mut self,
_step_result: &StepResult,
_gc: &mut GC<OnionObjectCell>,
) -> Result<(), RuntimeError> {
Err(RuntimeError::DetailedError(
"AsyncScheduler does not support receive".into(),
))
}
fn format_context(&self) -> String {
let mut output = Vec::new();
output.push(format!(
"-> AsyncScheduler Status:\n - Current Step: {}\n - Total Tasks in Queue: {}",
self.step,
self.queue.len()
));
if self.queue.is_empty() {
output.push(" - Queue is empty.".to_string());
} else {
output.push("--- Task Queue Details ---".to_string());
for (index, task) in self.queue.iter().enumerate() {
let next_run_step = {
let sched_interval = generate_sched_step(task.priority);
if self.step % sched_interval == 0 {
self.step } else {
self.step - (self.step % sched_interval) + sched_interval
}
};
let runnable_type = std::any::type_name_of_val(&*task.runnable);
let task_summary = format!(
" [Task #{}] Priority: {} (Next run at step {}), Type: {}",
index,
task.priority,
next_run_step,
runnable_type.split("::").last().unwrap_or(runnable_type) );
output.push(task_summary);
let inner_context = task.runnable.format_context();
for line in inner_context.lines() {
output.push(format!(" {}", line));
}
}
}
output.join("\n")
}
}