use std::cell::Cell;
use std::fmt;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicBool, Ordering};
use intrusive_collections::linked_list::LinkedListOps;
use intrusive_collections::{DefaultLinkOps, LinkOps};
pub struct AtomicLink {
locked: AtomicBool,
next: Cell<Option<NonNull<AtomicLink>>>,
prev: Cell<Option<NonNull<AtomicLink>>>,
}
unsafe impl Sync for AtomicLink {}
const UNLINKED_MARKER: Option<NonNull<AtomicLink>> = unsafe {
Some(NonNull::new_unchecked(
std::ptr::dangling_mut::<AtomicLink>(),
))
};
impl AtomicLink {
#[inline]
pub const fn new() -> AtomicLink {
AtomicLink {
locked: AtomicBool::new(false),
next: Cell::new(UNLINKED_MARKER),
prev: Cell::new(UNLINKED_MARKER),
}
}
}
impl DefaultLinkOps for AtomicLink {
type Ops = AtomicLinkOps;
const NEW: Self::Ops = AtomicLinkOps;
}
unsafe impl Send for AtomicLink {}
impl Clone for AtomicLink {
#[inline]
fn clone(&self) -> AtomicLink {
AtomicLink::new()
}
}
impl Default for AtomicLink {
#[inline]
fn default() -> AtomicLink {
AtomicLink::new()
}
}
impl fmt::Debug for AtomicLink {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.locked.load(Ordering::Relaxed) {
write!(f, "linked")
} else {
write!(f, "unlinked")
}
}
}
#[derive(Clone, Copy, Default, Debug)]
pub struct AtomicLinkOps;
unsafe impl LinkOps for AtomicLinkOps {
type LinkPtr = NonNull<AtomicLink>;
#[inline]
unsafe fn acquire_link(&mut self, ptr: Self::LinkPtr) -> bool {
!ptr.as_ref().locked.swap(true, Ordering::Acquire)
}
#[inline]
unsafe fn release_link(&mut self, ptr: Self::LinkPtr) {
ptr.as_ref().locked.store(false, Ordering::Release)
}
}
unsafe impl LinkedListOps for AtomicLinkOps {
#[inline]
unsafe fn next(&self, ptr: Self::LinkPtr) -> Option<Self::LinkPtr> {
ptr.as_ref().next.get()
}
#[inline]
unsafe fn prev(&self, ptr: Self::LinkPtr) -> Option<Self::LinkPtr> {
ptr.as_ref().prev.get()
}
#[inline]
unsafe fn set_next(&mut self, ptr: Self::LinkPtr, next: Option<Self::LinkPtr>) {
ptr.as_ref().next.set(next);
}
#[inline]
unsafe fn set_prev(&mut self, ptr: Self::LinkPtr, prev: Option<Self::LinkPtr>) {
ptr.as_ref().prev.set(prev);
}
}