use core::task::{RawWaker, RawWakerVTable, Waker};
#[cfg(not(loom))]
static READY_BITMAP: crate::sync::atomic::AtomicU32 = crate::sync::atomic::AtomicU32::new(0);
#[cfg(loom)]
loom::lazy_static! {
static ref READY_BITMAP: crate::sync::atomic::AtomicU32 = crate::sync::atomic::AtomicU32::new(0);
}
#[cfg(not(loom))]
static PRIORITY_QUEUES: [crate::sync::atomic::AtomicU32; 32] = [
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
];
#[cfg(loom)]
loom::lazy_static! {
static ref PRIORITY_QUEUES: [crate::sync::atomic::AtomicU32; 32] = [
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
crate::sync::atomic::AtomicU32::new(0),
];
}
#[cfg(not(loom))]
pub(crate) static EXECUTOR_PEND_FLAG: crate::sync::atomic::AtomicU32 =
crate::sync::atomic::AtomicU32::new(0);
#[cfg(loom)]
loom::lazy_static! {
pub(crate) static ref EXECUTOR_PEND_FLAG: crate::sync::atomic::AtomicU32 = crate::sync::atomic::AtomicU32::new(0);
}
pub fn reset() {
READY_BITMAP.store(0, crate::sync::atomic::Ordering::Release);
for q in PRIORITY_QUEUES.iter() {
q.store(0, crate::sync::atomic::Ordering::Release);
}
EXECUTOR_PEND_FLAG.store(0, crate::sync::atomic::Ordering::Release);
}
#[doc(hidden)]
pub fn mark_ready(id: crate::task::TaskId) {
let mask = 1u32 << (id.index() & 0x1F);
PRIORITY_QUEUES[id.priority() as usize].fetch_or(mask, crate::sync::atomic::Ordering::Release);
READY_BITMAP.fetch_or(
1u32 << id.priority(),
crate::sync::atomic::Ordering::Release,
);
EXECUTOR_PEND_FLAG.store(1, crate::sync::atomic::Ordering::Release);
}
#[doc(hidden)]
pub fn has_pending() -> bool {
EXECUTOR_PEND_FLAG.load(crate::sync::atomic::Ordering::Acquire) != 0
|| READY_BITMAP.load(crate::sync::atomic::Ordering::Acquire) != 0
}
#[doc(hidden)]
pub fn clear_pend() {
EXECUTOR_PEND_FLAG.store(0, crate::sync::atomic::Ordering::Release);
}
#[doc(hidden)]
pub fn next_ready() -> Option<crate::task::TaskId> {
let bitmap = READY_BITMAP.load(crate::sync::atomic::Ordering::Acquire);
if bitmap == 0 {
return None;
}
let prio = (31 - bitmap.leading_zeros()) as u8;
let queue = PRIORITY_QUEUES[prio as usize].load(crate::sync::atomic::Ordering::Acquire);
if queue == 0 {
READY_BITMAP.fetch_and(!(1u32 << prio), crate::sync::atomic::Ordering::AcqRel);
return None;
}
let bit = queue & queue.wrapping_neg();
let index = bit.trailing_zeros() as u8;
let prev =
PRIORITY_QUEUES[prio as usize].fetch_and(!bit, crate::sync::atomic::Ordering::AcqRel);
if prev == bit {
READY_BITMAP.fetch_and(!(1u32 << prio), crate::sync::atomic::Ordering::AcqRel);
}
Some(crate::task::TaskId::new(prio, index))
}
static TASK_ID_CELLS: [[crate::task::TaskId; 32]; 32] = {
let mut cells = [[crate::task::TaskId::new(0, 0); 32]; 32];
let mut p = 0;
while p < 32 {
let mut i = 0;
while i < 32 {
cells[p][i] = crate::task::TaskId::new(p as u8, i as u8);
i += 1;
}
p += 1;
}
cells
};
fn encode_waker_data(id: crate::task::TaskId) -> *const () {
core::ptr::addr_of!(TASK_ID_CELLS[id.priority() as usize][id.index() as usize]) as *const ()
}
fn decode_waker_data(data: *const ()) -> crate::task::TaskId {
unsafe { *(data as *const crate::task::TaskId) }
}
unsafe fn waker_clone(data: *const ()) -> RawWaker {
RawWaker::new(data, &WAKER_VTABLE)
}
unsafe fn waker_wake(data: *const ()) {
mark_ready(decode_waker_data(data));
}
unsafe fn waker_wake_by_ref(data: *const ()) {
mark_ready(decode_waker_data(data));
}
unsafe fn waker_drop(_data: *const ()) {
}
static WAKER_VTABLE: RawWakerVTable =
RawWakerVTable::new(waker_clone, waker_wake, waker_wake_by_ref, waker_drop);
pub fn broadcast_reschedule() {
let hart = crate::port::arch::hart_id();
for other in 0..crate::config::MAX_HARTS {
if other != hart {
crate::port::arch::request_reschedule_on(other);
}
}
}
pub fn wake_task(id: crate::task::TaskId) {
mark_ready(id);
broadcast_reschedule();
}
pub fn task_waker(id: crate::task::TaskId) -> Waker {
let data = encode_waker_data(id);
let raw = RawWaker::new(data, &WAKER_VTABLE);
unsafe { Waker::from_raw(raw) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_decode_roundtrip() {
crate::kernel_test! {
let data = encode_waker_data(crate::task::TaskId::new(5, 13));
let id = decode_waker_data(data);
assert_eq!(id.priority(), 5);
assert_eq!(id.index(), 13);
}
}
#[test]
fn mark_and_dequeue_single() {
crate::kernel_test! {
mark_ready(crate::task::TaskId::new(2, 0));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(2, 0)));
assert_eq!(next_ready(), None);
}
}
#[test]
fn priority_ordering() {
crate::kernel_test! {
mark_ready(crate::task::TaskId::new(1, 0));
mark_ready(crate::task::TaskId::new(5, 0));
mark_ready(crate::task::TaskId::new(3, 0));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(5, 0)));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(3, 0)));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(1, 0)));
assert_eq!(next_ready(), None);
}
}
#[test]
fn multiple_tasks_same_priority() {
crate::kernel_test! {
mark_ready(crate::task::TaskId::new(3, 0));
mark_ready(crate::task::TaskId::new(3, 1));
mark_ready(crate::task::TaskId::new(3, 2));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(3, 0)));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(3, 1)));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(3, 2)));
assert_eq!(next_ready(), None);
}
}
#[test]
fn interleaved_wake() {
crate::kernel_test! {
mark_ready(crate::task::TaskId::new(2, 0));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(2, 0)));
assert_eq!(next_ready(), None);
mark_ready(crate::task::TaskId::new(2, 0));
mark_ready(crate::task::TaskId::new(4, 1));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(4, 1)));
assert_eq!(next_ready(), Some(crate::task::TaskId::new(2, 0)));
assert_eq!(next_ready(), None);
}
}
#[test]
fn has_pending_detects_work() {
crate::kernel_test! {
assert!(!has_pending());
mark_ready(crate::task::TaskId::new(0, 0));
assert!(has_pending());
next_ready();
clear_pend();
assert!(!has_pending());
}
}
}