feat: linked_list: allow to have multiple links in a same struct

This commit is contained in:
Paul Pan 2024-08-28 13:04:22 +08:00
parent a867e77e3c
commit c352a0b8c6
4 changed files with 185 additions and 78 deletions

View File

@ -42,12 +42,14 @@ impl RawCap {
}
}
pub const CAP_ENTRY_LINK_ID: usize = 1;
pub struct CapEntry {
pub cap: RawCap,
pub link: Link<Self>,
pub link: Link<Self, CAP_ENTRY_LINK_ID>,
}
LinkHelperImpl!(CapEntry: link);
LinkHelperImpl!(CapEntry { link } => CAP_ENTRY_LINK_ID);
impl CapEntry {
pub fn new(cap: RawCap) -> Self {

View File

@ -24,6 +24,8 @@ pub enum ThreadState {
Idle,
}
pub const SCHED_QUEUE_LINK_ID: usize = 1;
#[repr(C)]
pub struct TcbObject {
pub trapframe: TrapContext, // must be the first field
@ -38,10 +40,11 @@ pub struct TcbObject {
state: ThreadState,
time_tick: usize,
tid: usize,
pub link: Link<Self>,
pub sched_queue: Link<Self, SCHED_QUEUE_LINK_ID>,
}
LinkHelperImpl!(TcbObject:link);
LinkHelperImpl!(TcbObject { sched_queue } => SCHED_QUEUE_LINK_ID);
const_assert_eq!(ObjectType::TCB.size(0), ObjectType::TCB.size(0x42));
const_assert!(core::mem::size_of::<TcbObject>() <= ObjectType::TCB.size(0));
@ -59,7 +62,7 @@ impl TcbObject {
state: ThreadState::Inactive,
time_tick: 0,
tid: TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst),
link: Link::default(),
sched_queue: Link::default(),
}
}

View File

@ -2,6 +2,7 @@ use crate::objects::*;
use core::sync::atomic::AtomicPtr;
use log::{error, trace};
use spin::lazy::Lazy;
use tcb::SCHED_QUEUE_LINK_ID;
use utils::{container_of_mut, linked_list::Link};
#[thread_local]
@ -17,7 +18,7 @@ pub static SCHEDULER: Scheduler = Scheduler::new();
// TODO: add a shared buffer to transfer TCB between cores
pub struct Scheduler {
head: Link<TcbObject>,
head: Link<TcbObject, SCHED_QUEUE_LINK_ID>,
}
impl Scheduler {
@ -27,7 +28,7 @@ impl Scheduler {
pub fn init(&self) {
unsafe {
let head = Some(AtomicPtr::new(container_of_mut!(&self.head, TcbObject, link)));
let head = Some(AtomicPtr::new(container_of_mut!(&self.head, TcbObject, sched_queue)));
self.head.set_next(&head);
self.head.set_prev(&head);
}
@ -48,7 +49,7 @@ impl Scheduler {
// put to the end of the queue
if next.timetick() == 0 || !next.schedulable() {
next.link.detach();
next.sched_queue.detach();
self.head.prepend(next);
}
}

View File

@ -13,35 +13,39 @@ macro_rules! as_mut_ptr {
#[macro_export]
macro_rules! LinkHelperImpl {
($type:ty : $field:ident) => {
impl LinkHelper for $type {
($type:ty { $field:ident } => $id:expr) => {
impl LinkHelper<$id> for $type {
const LINK_OFFSET: usize = core::mem::offset_of!($type, $field) as usize;
}
};
}
pub trait LinkHelper: Sized {
pub trait LinkHelper<const ID: usize>: Sized {
const LINK_OFFSET: usize;
/// # Safety
/// LINK_OFFSET must be a valid field offset of Self
unsafe fn get_link(&self) -> &Link<Self> {
&*(to_field_offset!(self, Link<Self>, Self::LINK_OFFSET))
unsafe fn get_link(&self) -> &Link<Self, ID> {
&*(to_field_offset!(self, Link<Self, ID>, Self::LINK_OFFSET))
}
}
pub struct Link<T: LinkHelper> {
unsafe fn get_link<T: LinkHelper<ID>, const ID: usize>(node: &T) -> &Link<T, ID> {
<T as LinkHelper<ID>>::get_link(node)
}
pub struct Link<T: LinkHelper<ID>, const ID: usize> {
pub prev: Option<AtomicPtr<T>>,
pub next: Option<AtomicPtr<T>>,
}
impl<T: LinkHelper> Default for Link<T> {
impl<T: LinkHelper<ID>, const ID: usize> Default for Link<T, ID> {
fn default() -> Self {
Self::new()
}
}
impl<T: LinkHelper> Clone for Link<T> {
impl<T: LinkHelper<ID>, const ID: usize> Clone for Link<T, ID> {
fn clone(&self) -> Self {
Self {
prev: copy_ptr(&self.prev),
@ -50,7 +54,7 @@ impl<T: LinkHelper> Clone for Link<T> {
}
}
impl<T: LinkHelper> Link<T> {
impl<T: LinkHelper<ID>, const ID: usize> Link<T, ID> {
pub const fn new() -> Self {
Self { prev: None, next: None }
}
@ -104,12 +108,12 @@ impl<T: LinkHelper> Link<T> {
pub fn prepend(&self, new: &T) {
unsafe {
// setup new's link
new.get_link().set_prev(self.prev_raw());
new.get_link().set_next(&Some(AtomicPtr::new(as_mut_ptr!(self.object()))));
get_link(new).set_prev(self.prev_raw());
get_link(new).set_next(&Some(AtomicPtr::new(as_mut_ptr!(self.object()))));
// setup prev's link
if let Some(prev) = self.prev_raw() {
load_ptr(prev).get_link().set_next(&Some(AtomicPtr::new(as_mut_ptr!(new))));
get_link(load_ptr(prev)).set_next(&Some(AtomicPtr::new(as_mut_ptr!(new))));
}
// setup self's link
@ -120,12 +124,12 @@ impl<T: LinkHelper> Link<T> {
pub fn append(&self, new: &T) {
unsafe {
// setup new's link
new.get_link().set_prev(&Some(AtomicPtr::new(as_mut_ptr!(self.object()))));
new.get_link().set_next(self.next_raw());
get_link(new).set_prev(&Some(AtomicPtr::new(as_mut_ptr!(self.object()))));
get_link(new).set_next(self.next_raw());
// setup next's link
if let Some(next) = self.next_raw() {
load_ptr(next).get_link().set_prev(&Some(AtomicPtr::new(as_mut_ptr!(new))))
get_link(load_ptr(next)).set_prev(&Some(AtomicPtr::new(as_mut_ptr!(new))))
}
// setup self's link
@ -137,12 +141,12 @@ impl<T: LinkHelper> Link<T> {
unsafe {
// setup prev's link
if let Some(prev) = self.prev_raw() {
load_ptr(prev).get_link().set_next(self.next_raw())
get_link(load_ptr(prev)).set_next(self.next_raw())
}
// setup next's link
if let Some(next) = self.next_raw() {
load_ptr(next).get_link().set_prev(self.prev_raw())
get_link(load_ptr(next)).set_prev(self.prev_raw())
}
// setup self's link
@ -167,94 +171,191 @@ mod tests {
use super::*;
struct Node {
link: Link<Node>,
link1: Link<Node, 1>,
link2: Link<Node, 2>,
value: i32,
}
LinkHelperImpl!(Node: link);
LinkHelperImpl!(Node { link1 } => 1);
LinkHelperImpl!(Node { link2 } => 2);
#[test]
fn test_linked_list() {
let node1 = Node {
link: Link::default(),
let n1 = Node {
link1: Link::default(),
link2: Link::default(),
value: 1,
};
let node2 = Node {
link: Link::default(),
let n2 = Node {
link1: Link::default(),
link2: Link::default(),
value: 2,
};
let node3 = Node {
link: Link::default(),
let n3 = Node {
link1: Link::default(),
link2: Link::default(),
value: 3,
};
node1.link.append(&node2);
node2.link.append(&node3);
// link1: node1 <-> node2 <-> node3
n1.link1.append(&n2);
n2.link1.append(&n3);
// link2: node1 <-> node3 <-> node2
n1.link2.append(&n3);
n3.link2.append(&n2);
// link1: check object
unsafe {
// check object
assert_eq!(as_mut_ptr!(node1.link.object() => Node), as_mut_ptr!(&node1 => Node));
assert_eq!(node1.link.object().value, 1);
assert_eq!(as_mut_ptr!(node2.link.object() => Node), as_mut_ptr!(&node2 => Node));
assert_eq!(node2.link.object().value, 2);
assert_eq!(as_mut_ptr!(node3.link.object() => Node), as_mut_ptr!(&node3 => Node));
assert_eq!(node3.link.object().value, 3);
assert_eq!(as_mut_ptr!(n1.link1.object() => Node), as_mut_ptr!(&n1 => Node));
assert_eq!(n1.link1.object().value, 1);
assert_eq!(as_mut_ptr!(n2.link1.object() => Node), as_mut_ptr!(&n2 => Node));
assert_eq!(n2.link1.object().value, 2);
assert_eq!(as_mut_ptr!(n3.link1.object() => Node), as_mut_ptr!(&n3 => Node));
assert_eq!(n3.link1.object().value, 3);
}
// link2: check object
unsafe {
// check get_link
assert_eq!(as_mut_ptr!(node1.get_link() => Link<Node>), as_mut_ptr!(&node1.link));
assert_eq!(as_mut_ptr!(node2.get_link() => Link<Node>), as_mut_ptr!(&node2.link));
assert_eq!(as_mut_ptr!(node3.get_link() => Link<Node>), as_mut_ptr!(&node3.link));
assert_eq!(as_mut_ptr!(n1.link2.object() => Node), as_mut_ptr!(&n1 => Node));
assert_eq!(n1.link2.object().value, 1);
assert_eq!(as_mut_ptr!(n2.link2.object() => Node), as_mut_ptr!(&n2 => Node));
assert_eq!(n2.link2.object().value, 2);
assert_eq!(as_mut_ptr!(n3.link2.object() => Node), as_mut_ptr!(&n3 => Node));
assert_eq!(n3.link2.object().value, 3);
}
{
// check next link
assert!(node1.link.next_raw().is_some());
assert_eq!(
node1.link.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&node2)
);
assert!(node2.link.next_raw().is_some());
assert_eq!(
node2.link.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&node3)
);
assert!(node3.link.next_raw().is_none());
// link1: check get_link
unsafe {
assert_eq!(as_mut_ptr!(get_link::<_, 1>(&n1) => Link<Node, 1>), as_mut_ptr!(&n1.link1));
assert_eq!(as_mut_ptr!(get_link::<_, 1>(&n2) => Link<Node, 1>), as_mut_ptr!(&n2.link1));
assert_eq!(as_mut_ptr!(get_link::<_, 1>(&n3) => Link<Node, 1>), as_mut_ptr!(&n3.link1));
}
// link2: check get_link
unsafe {
assert_eq!(as_mut_ptr!(get_link::<_, 2>(&n1) => Link<Node, 2>), as_mut_ptr!(&n1.link2));
assert_eq!(as_mut_ptr!(get_link::<_, 2>(&n2) => Link<Node, 2>), as_mut_ptr!(&n2.link2));
assert_eq!(as_mut_ptr!(get_link::<_, 2>(&n3) => Link<Node, 2>), as_mut_ptr!(&n3.link2));
}
// link1: check next link
{
// check prev link
assert!(node1.link.prev_raw().is_none());
assert!(node2.link.prev_raw().is_some());
// node1 -> node2
assert!(n1.link1.next_raw().is_some());
assert_eq!(
node2.link.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&node1)
n1.link1.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n2)
);
assert!(node3.link.prev_raw().is_some());
// node2 -> node3
assert!(n2.link1.next_raw().is_some());
assert_eq!(
node3.link.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&node2)
n2.link1.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n3)
);
// node3 -> None
assert!(n3.link1.next_raw().is_none());
}
// link2: check next link
{
// node1 -> node3
assert!(n1.link2.next_raw().is_some());
assert_eq!(
n1.link2.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n3)
);
// node3 -> node2
assert!(n3.link2.next_raw().is_some());
assert_eq!(
n3.link2.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n2)
);
// node2 -> None
assert!(n2.link2.next_raw().is_none());
}
// link1: check prev link
{
// none <- node1
assert!(n1.link1.prev_raw().is_none());
// node1 <- node2
assert!(n2.link1.prev_raw().is_some());
assert_eq!(
n2.link1.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n1)
);
// node2 <- node3
assert!(n3.link1.prev_raw().is_some());
assert_eq!(
n3.link1.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n2)
);
}
// link2: check prev link
{
// check detach
node2.link.detach();
// none <- node1
assert!(n1.link2.prev_raw().is_none());
assert!(node2.link.next_raw().is_none());
assert!(node2.link.prev_raw().is_none());
assert!(node1.link.next_raw().is_some());
// node1 <- node3
assert!(n3.link2.prev_raw().is_some());
assert_eq!(
node1.link.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&node3)
n3.link2.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n1)
);
assert!(node3.link.prev_raw().is_some());
// node3 <- node2
assert!(n2.link2.prev_raw().is_some());
assert_eq!(
node3.link.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&node1)
n2.link2.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n3)
);
}
// link1: check detach
{
n2.link1.detach();
assert!(n2.link1.next_raw().is_none());
assert!(n2.link1.prev_raw().is_none());
assert!(n1.link1.next_raw().is_some());
assert_eq!(
n1.link1.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n3)
);
assert!(n3.link1.prev_raw().is_some());
assert_eq!(
n3.link1.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n1)
);
}
// link2: check detach
{
n3.link2.detach();
assert!(n3.link2.next_raw().is_none());
assert!(n3.link2.prev_raw().is_none());
assert!(n1.link2.next_raw().is_some());
assert_eq!(
n1.link2.next_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n2)
);
assert!(n2.link2.prev_raw().is_some());
assert_eq!(
n2.link2.prev_raw().as_ref().unwrap().load(Ordering::Acquire),
as_mut_ptr!(&n1)
);
}
}