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 struct CapEntry {
pub cap: RawCap, 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 { impl CapEntry {
pub fn new(cap: RawCap) -> Self { pub fn new(cap: RawCap) -> Self {

View File

@ -24,6 +24,8 @@ pub enum ThreadState {
Idle, Idle,
} }
pub const SCHED_QUEUE_LINK_ID: usize = 1;
#[repr(C)] #[repr(C)]
pub struct TcbObject { pub struct TcbObject {
pub trapframe: TrapContext, // must be the first field pub trapframe: TrapContext, // must be the first field
@ -38,10 +40,11 @@ pub struct TcbObject {
state: ThreadState, state: ThreadState,
time_tick: usize, time_tick: usize,
tid: 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_eq!(ObjectType::TCB.size(0), ObjectType::TCB.size(0x42));
const_assert!(core::mem::size_of::<TcbObject>() <= ObjectType::TCB.size(0)); const_assert!(core::mem::size_of::<TcbObject>() <= ObjectType::TCB.size(0));
@ -59,7 +62,7 @@ impl TcbObject {
state: ThreadState::Inactive, state: ThreadState::Inactive,
time_tick: 0, time_tick: 0,
tid: TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst), 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 core::sync::atomic::AtomicPtr;
use log::{error, trace}; use log::{error, trace};
use spin::lazy::Lazy; use spin::lazy::Lazy;
use tcb::SCHED_QUEUE_LINK_ID;
use utils::{container_of_mut, linked_list::Link}; use utils::{container_of_mut, linked_list::Link};
#[thread_local] #[thread_local]
@ -17,7 +18,7 @@ pub static SCHEDULER: Scheduler = Scheduler::new();
// TODO: add a shared buffer to transfer TCB between cores // TODO: add a shared buffer to transfer TCB between cores
pub struct Scheduler { pub struct Scheduler {
head: Link<TcbObject>, head: Link<TcbObject, SCHED_QUEUE_LINK_ID>,
} }
impl Scheduler { impl Scheduler {
@ -27,7 +28,7 @@ impl Scheduler {
pub fn init(&self) { pub fn init(&self) {
unsafe { 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_next(&head);
self.head.set_prev(&head); self.head.set_prev(&head);
} }
@ -48,7 +49,7 @@ impl Scheduler {
// put to the end of the queue // put to the end of the queue
if next.timetick() == 0 || !next.schedulable() { if next.timetick() == 0 || !next.schedulable() {
next.link.detach(); next.sched_queue.detach();
self.head.prepend(next); self.head.prepend(next);
} }
} }

View File

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