use core::ptr::null_mut;
use core::sync::atomic::{AtomicPtr, AtomicU32, AtomicUsize, Ordering};
use crate::task::Task;
const MAX_NUM_TASKS: usize = 32;
static CURR_TICK: AtomicU32 = AtomicU32::new(0);
static WAKE_TICK: AtomicU32 = AtomicU32::new(0);
static CURR_TASK: AtomicUsize = AtomicUsize::new(MAX_NUM_TASKS);
static IDLE_TASK: Task = Task::new(MAX_NUM_TASKS, u8::MAX);
static TASK_TABLE: [AtomicPtr<Task>; MAX_NUM_TASKS] =
[const { AtomicPtr::new(null_mut()) }; MAX_NUM_TASKS];
fn get_task(id: usize) -> Option<&'static Task> {
assert!(id <= MAX_NUM_TASKS, "Invalid task");
if id == MAX_NUM_TASKS {
return Some(&IDLE_TASK);
}
let task = TASK_TABLE[id].load(Ordering::Relaxed);
if task.is_null() {
None
} else {
unsafe { Some(&*TASK_TABLE[id].load(Ordering::Relaxed)) }
}
}
pub fn init(idle_stack_ptr: u32) {
IDLE_TASK.stack_ptr.store(idle_stack_ptr, Ordering::Relaxed);
}
pub fn create(task: &'static Task, task_stack_ptr: u32) {
assert!(task.id < MAX_NUM_TASKS, "Invalid task");
task.stack_ptr.store(task_stack_ptr, Ordering::Relaxed);
TASK_TABLE[task.id].store(task as *const _ as *mut _, Ordering::Relaxed);
}
pub fn delete(id: usize) -> bool {
assert!(id < MAX_NUM_TASKS, "Invalid task");
let task = get_task(id).expect("Task does not exist");
let is_current_task = task == get_current_task().unwrap();
TASK_TABLE[id].store(null_mut(), Ordering::Relaxed);
is_current_task
}
pub fn get_current_task() -> Option<&'static Task> {
let id = CURR_TASK.load(Ordering::Relaxed);
if id >= MAX_NUM_TASKS {
Some(&IDLE_TASK)
} else {
get_task(id)
}
}
pub fn get_current_tick() -> u32 {
CURR_TICK.load(Ordering::Relaxed)
}
pub fn sleep(delay: u32) -> bool {
if delay > 0 {
let curr_task = get_current_task().unwrap();
let curr_tick = get_current_tick();
let wake_tick = curr_tick + delay;
curr_task.sleep(wake_tick);
let next_tick = WAKE_TICK.load(Ordering::Relaxed);
if next_tick < curr_tick || next_tick > wake_tick {
WAKE_TICK.store(wake_tick, Ordering::Relaxed);
}
return true;
}
false
}
pub fn suspend(id: usize) -> bool {
let task = get_task(id).expect("Task does not exist");
task.suspend();
task == get_current_task().unwrap()
}
pub fn resume(id: usize) {
let task = get_task(id).expect("Task does not exist");
task.ready();
}
pub fn run_scheduler(curr_task_stack_ptr: u32) -> u32 {
let curr_task = get_current_task();
let curr_tick = get_current_tick();
match curr_task {
Some(task) => task.stack_ptr.store(curr_task_stack_ptr, Ordering::Relaxed),
None => (),
};
let mut next_task = &IDLE_TASK;
for i in 0..MAX_NUM_TASKS {
match get_task(i) {
Some(task) => {
if task.is_sleep() && task.wake_tick() <= curr_tick {
task.ready();
}
if task.is_ready() && task.priority < next_task.priority {
next_task = task;
}
}
None => (),
}
}
if curr_task.is_none() || next_task != curr_task.unwrap() {
CURR_TASK.store(next_task.id, Ordering::Relaxed);
next_task.stack_ptr.load(Ordering::Relaxed)
} else {
curr_task_stack_ptr
}
}
pub fn update_tick(elapsed: u32) -> bool {
let curr_tick = CURR_TICK.fetch_add(elapsed, Ordering::Relaxed) + elapsed;
let wake_tick = WAKE_TICK.load(Ordering::Relaxed);
wake_tick <= curr_tick
}
#[cfg(test)]
mod tests {
use super::*;
static TASK0: Task = Task::new(0, 10);
static TASK1: Task = Task::new(1, 11);
static TASK2: Task = Task::new(2, 12);
#[test]
fn test_create() {
create(&TASK0, 0xDEADBEEF);
create(&TASK1, 0xDEADBEEF);
create(&TASK2, 0xDEADBEEF);
assert_eq!(get_task(0).unwrap(), &TASK0);
assert_eq!(get_task(1).unwrap(), &TASK1);
assert_eq!(get_task(2).unwrap(), &TASK2);
assert_eq!(get_task(3), None);
}
#[test]
fn test_delete() {
create(&TASK0, 0xDEADBEEF);
assert_eq!(delete(0), false);
assert_eq!(get_task(0), None);
}
#[test]
fn test_suspend_and_resume() {
create(&TASK0, 0xDEADBEEF);
assert_eq!(suspend(0), false);
assert!(TASK0.is_suspended());
resume(0);
assert!(TASK0.is_ready())
}
#[test]
fn test_run_scheduler() {
let task0_stack_ptr = 0xAA;
let task1_stack_ptr = 0xBB;
let task2_stack_ptr = 0xCC;
create(&TASK0, task0_stack_ptr);
create(&TASK1, task1_stack_ptr);
create(&TASK2, task2_stack_ptr);
assert_eq!(run_scheduler(task0_stack_ptr), task0_stack_ptr);
assert_eq!(get_current_task(), Some(&TASK0));
TASK0.sleep(5);
assert_eq!(run_scheduler(task0_stack_ptr), task1_stack_ptr);
assert_eq!(get_current_task(), Some(&TASK1));
TASK1.sleep(5);
assert_eq!(run_scheduler(task1_stack_ptr), task2_stack_ptr);
assert_eq!(get_current_task(), Some(&TASK2));
}
#[test]
fn test_update_tick() {
update_tick(3);
assert_eq!(get_current_tick(), 3);
}
}