use crate::arch;
use crate::arch::mm::VirtAddr;
use crate::arch::percore::*;
use crate::arch::processor::msb;
use crate::arch::scheduler::{TaskStacks, TaskTLS};
use crate::scheduler::CoreId;
use alloc::collections::{LinkedList, VecDeque};
use alloc::rc::Rc;
use core::cell::RefCell;
use core::convert::TryInto;
use core::fmt;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TaskStatus {
TaskInvalid,
TaskReady,
TaskRunning,
TaskBlocked,
TaskFinished,
TaskIdle,
}
#[derive(Clone, Copy, PartialEq)]
pub enum WakeupReason {
Custom,
Timer,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub struct TaskId(u32);
impl TaskId {
pub const fn into(self) -> u32 {
self.0
}
pub const fn from(x: u32) -> Self {
TaskId(x)
}
}
impl fmt::Display for TaskId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub struct Priority(u8);
impl Priority {
pub const fn into(self) -> u8 {
self.0
}
pub const fn from(x: u8) -> Self {
Priority(x)
}
}
impl fmt::Display for Priority {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[allow(dead_code)]
pub const HIGH_PRIO: Priority = Priority::from(3);
#[allow(dead_code)]
pub const NORMAL_PRIO: Priority = Priority::from(2);
#[allow(dead_code)]
pub const LOW_PRIO: Priority = Priority::from(1);
#[allow(dead_code)]
pub const IDLE_PRIO: Priority = Priority::from(0);
pub const NO_PRIORITIES: usize = 31;
#[derive(Copy, Clone, Debug)]
pub struct TaskHandle {
id: TaskId,
priority: Priority,
core_id: CoreId,
}
impl TaskHandle {
pub fn new(id: TaskId, priority: Priority, core_id: CoreId) -> Self {
Self {
id,
priority,
core_id,
}
}
pub fn get_core_id(&self) -> CoreId {
self.core_id
}
pub fn get_id(&self) -> TaskId {
self.id
}
pub fn get_priority(&self) -> Priority {
self.priority
}
}
pub struct TaskHandlePriorityQueue {
queues: [Option<VecDeque<TaskHandle>>; NO_PRIORITIES],
prio_bitmap: u64,
}
impl TaskHandlePriorityQueue {
pub const fn new() -> Self {
Self {
queues: [
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None,
],
prio_bitmap: 0,
}
}
pub fn push(&mut self, task: TaskHandle) {
let i = task.priority.into() as usize;
self.prio_bitmap |= (1 << i) as u64;
if let Some(queue) = &mut self.queues[i] {
queue.push_back(task);
} else {
let mut queue = VecDeque::new();
queue.push_back(task);
self.queues[i] = Some(queue);
}
}
fn pop_from_queue(&mut self, queue_index: usize) -> Option<TaskHandle> {
if let Some(queue) = &mut self.queues[queue_index] {
let task = queue.pop_front();
if queue.is_empty() {
self.prio_bitmap &= !(1 << queue_index as u64);
}
task
} else {
None
}
}
pub fn pop(&mut self) -> Option<TaskHandle> {
if let Some(i) = msb(self.prio_bitmap) {
return self.pop_from_queue(i as usize);
}
None
}
pub fn remove(&mut self, task: TaskHandle) {
let queue_index = task.priority.into() as usize;
if let Some(queue) = &mut self.queues[queue_index] {
let mut i = 0;
while i != queue.len() {
if queue[i].id == task.id {
queue.remove(i);
} else {
i += 1;
}
}
if queue.is_empty() {
self.prio_bitmap &= !(1 << queue_index as u64);
}
}
}
}
struct QueueHead {
head: Option<Rc<RefCell<Task>>>,
tail: Option<Rc<RefCell<Task>>>,
}
impl QueueHead {
pub const fn new() -> Self {
Self {
head: None,
tail: None,
}
}
}
impl Default for QueueHead {
fn default() -> Self {
Self {
head: None,
tail: None,
}
}
}
pub struct PriorityTaskQueue {
queues: [QueueHead; NO_PRIORITIES],
prio_bitmap: u64,
}
impl PriorityTaskQueue {
pub const fn new() -> PriorityTaskQueue {
PriorityTaskQueue {
queues: [
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
QueueHead::new(),
],
prio_bitmap: 0,
}
}
pub fn push(&mut self, task: Rc<RefCell<Task>>) {
let i = task.borrow().prio.into() as usize;
self.prio_bitmap |= (1 << i) as u64;
match self.queues[i].tail {
None => {
self.queues[i].head = Some(task.clone());
let mut borrow = task.borrow_mut();
borrow.next = None;
borrow.prev = None;
}
Some(ref mut tail) => {
tail.borrow_mut().next = Some(task.clone());
let mut borrow = task.borrow_mut();
borrow.next = None;
borrow.prev = Some(tail.clone());
}
}
self.queues[i].tail = Some(task);
}
fn pop_from_queue(&mut self, queue_index: usize) -> Option<Rc<RefCell<Task>>> {
let new_head;
let task;
match self.queues[queue_index].head {
None => {
return None;
}
Some(ref mut head) => {
let mut borrow = head.borrow_mut();
match borrow.next {
Some(ref mut nhead) => {
nhead.borrow_mut().prev = None;
}
None => {}
}
new_head = borrow.next.clone();
borrow.next = None;
borrow.prev = None;
task = head.clone();
}
}
self.queues[queue_index].head = new_head;
if self.queues[queue_index].head.is_none() {
self.queues[queue_index].tail = None;
self.prio_bitmap &= !(1 << queue_index as u64);
}
Some(task)
}
pub fn pop(&mut self) -> Option<Rc<RefCell<Task>>> {
if let Some(i) = msb(self.prio_bitmap) {
return self.pop_from_queue(i as usize);
}
None
}
pub fn pop_with_prio(&mut self, prio: Priority) -> Option<Rc<RefCell<Task>>> {
if let Some(i) = msb(self.prio_bitmap) {
if i >= u64::from(prio.into()) {
return self.pop_from_queue(i as usize);
}
}
None
}
pub fn get_highest_priority(&self) -> Priority {
if let Some(i) = msb(self.prio_bitmap) {
Priority::from(i.try_into().unwrap())
} else {
IDLE_PRIO
}
}
}
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
pub struct Task {
pub id: TaskId,
pub status: TaskStatus,
pub prio: Priority,
pub last_stack_pointer: VirtAddr,
pub user_stack_pointer: VirtAddr,
pub last_fpu_state: arch::processor::FPUState,
pub core_id: CoreId,
pub stacks: TaskStacks,
pub next: Option<Rc<RefCell<Task>>>,
pub prev: Option<Rc<RefCell<Task>>>,
pub tls: Option<TaskTLS>,
pub last_wakeup_reason: WakeupReason,
#[cfg(feature = "newlib")]
pub lwip_errno: i32,
}
pub trait TaskFrame {
fn create_stack_frame(&mut self, func: extern "C" fn(usize), arg: usize);
}
impl Task {
pub fn new(
tid: TaskId,
core_id: CoreId,
task_status: TaskStatus,
task_prio: Priority,
stack_size: usize,
) -> Task {
debug!("Creating new task {} on core {}", tid, core_id);
Task {
id: tid,
status: task_status,
prio: task_prio,
last_stack_pointer: VirtAddr(0u64),
user_stack_pointer: VirtAddr(0u64),
last_fpu_state: arch::processor::FPUState::new(),
core_id,
stacks: TaskStacks::new(stack_size),
next: None,
prev: None,
tls: None,
last_wakeup_reason: WakeupReason::Custom,
#[cfg(feature = "newlib")]
lwip_errno: 0,
}
}
pub fn new_idle(tid: TaskId, core_id: CoreId) -> Task {
debug!("Creating idle task {}", tid);
Task {
id: tid,
status: TaskStatus::TaskIdle,
prio: IDLE_PRIO,
last_stack_pointer: VirtAddr(0u64),
user_stack_pointer: VirtAddr(0u64),
last_fpu_state: arch::processor::FPUState::new(),
core_id,
stacks: TaskStacks::from_boot_stacks(),
next: None,
prev: None,
tls: None,
last_wakeup_reason: WakeupReason::Custom,
#[cfg(feature = "newlib")]
lwip_errno: 0,
}
}
pub fn clone(tid: TaskId, core_id: CoreId, task: &Task) -> Task {
debug!("Cloning task {} from task {}", tid, task.id);
Task {
id: tid,
status: TaskStatus::TaskReady,
prio: task.prio,
last_stack_pointer: VirtAddr(0u64),
user_stack_pointer: VirtAddr(0u64),
last_fpu_state: arch::processor::FPUState::new(),
core_id,
stacks: task.stacks.clone(),
next: None,
prev: None,
tls: task.tls.clone(),
last_wakeup_reason: task.last_wakeup_reason,
#[cfg(feature = "newlib")]
lwip_errno: 0,
}
}
}
struct BlockedTask {
task: Rc<RefCell<Task>>,
wakeup_time: Option<u64>,
}
pub struct BlockedTaskQueue {
list: LinkedList<BlockedTask>,
}
impl BlockedTaskQueue {
pub const fn new() -> Self {
Self {
list: LinkedList::new(),
}
}
fn wakeup_task(task: Rc<RefCell<Task>>, reason: WakeupReason) {
{
let mut borrowed = task.borrow_mut();
debug!(
"Waking up task {} on core {}",
borrowed.id, borrowed.core_id
);
assert!(
borrowed.core_id == core_id(),
"Try to wake up task {} on the wrong core {} != {}",
borrowed.id,
borrowed.core_id,
core_id()
);
assert!(
borrowed.status == TaskStatus::TaskBlocked,
"Trying to wake up task {} which is not blocked",
borrowed.id
);
borrowed.status = TaskStatus::TaskReady;
borrowed.last_wakeup_reason = reason;
}
core_scheduler().ready_queue.push(task);
}
pub fn add(&mut self, task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) {
{
let mut borrowed = task.borrow_mut();
debug!("Blocking task {}", borrowed.id);
assert_eq!(
borrowed.status,
TaskStatus::TaskRunning,
"Trying to block task {} which is not running",
borrowed.id
);
borrowed.status = TaskStatus::TaskBlocked;
}
let new_node = BlockedTask { task, wakeup_time };
if let Some(wt) = wakeup_time {
let mut first_task = true;
let mut cursor = self.list.cursor_front_mut();
while let Some(node) = cursor.current() {
let node_wakeup_time = node.wakeup_time;
if node_wakeup_time.is_none() || wt < node_wakeup_time.unwrap() {
cursor.insert_before(new_node);
if first_task {
arch::set_oneshot_timer(wakeup_time);
}
return;
}
first_task = false;
cursor.move_next();
}
} else {
self.list.push_back(new_node);
}
}
pub fn custom_wakeup(&mut self, task: TaskHandle) {
let mut first_task = true;
let mut cursor = self.list.cursor_front_mut();
while let Some(node) = cursor.current() {
if node.task.borrow().id == task.get_id() {
Self::wakeup_task(node.task.clone(), WakeupReason::Custom);
cursor.remove_current();
if first_task {
if let Some(next_node) = cursor.current() {
arch::set_oneshot_timer(next_node.wakeup_time);
}
}
break;
}
first_task = false;
cursor.move_next();
}
}
pub fn handle_waiting_tasks(&mut self) {
let time = arch::processor::get_timer_ticks();
let mut cursor = self.list.cursor_front_mut();
while let Some(node) = cursor.current() {
let node_wakeup_time = node.wakeup_time;
if node_wakeup_time.is_none() || time < node_wakeup_time.unwrap() {
arch::set_oneshot_timer(node_wakeup_time);
break;
}
Self::wakeup_task(node.task.clone(), WakeupReason::Timer);
cursor.remove_current();
}
}
}