mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
feat: linked_list: allow to have multiple links in a same struct
This commit is contained in:
parent
a867e77e3c
commit
c352a0b8c6
@ -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 {
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user