use std::alloc::{Layout, dealloc};
use std::mem::{ManuallyDrop, offset_of};
use std::ptr;
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering;
pub(super) struct Link {
next_or_ref: AtomicPtr<()>,
dealloc_fn: fn(*mut Link),
}
pub(super) struct DeferredClosure<F: 'static + FnOnce()> {
f: ManuallyDrop<F>,
link: Link,
}
impl Link {
#[inline]
pub(super) const fn new_shared(dealloc_fn: fn(*mut Link)) -> Self {
Link {
next_or_ref: AtomicPtr::new(ptr::without_provenance_mut(1)),
dealloc_fn,
}
}
#[inline]
pub(super) const fn new_unique(dealloc_fn: fn(*mut Link)) -> Self {
Link {
next_or_ref: AtomicPtr::new(ptr::without_provenance_mut(0)),
dealloc_fn,
}
}
#[inline]
pub(super) const fn ref_cnt(&self) -> &AtomicPtr<()> {
&self.next_or_ref
}
#[inline]
pub(super) const fn dealloc_fn(&self) -> fn(*mut Link) {
self.dealloc_fn
}
#[inline]
pub(super) fn next_ptr(&self, mo: Ordering) -> *mut Link {
self.next_or_ref.load(mo).cast::<Link>()
}
#[inline]
pub(super) fn set_next_ptr(&self, next_ptr: *mut Link, mo: Ordering) {
self.next_or_ref.store(next_ptr.cast::<()>(), mo);
}
#[inline]
pub(super) fn cast<T>(ptr: *mut Link, offset: usize) -> *mut T {
#[allow(clippy::cast_ptr_alignment)]
ptr.map_addr(|addr| addr - offset).cast::<T>()
}
}
impl<F: 'static + FnOnce()> DeferredClosure<F> {
#[inline]
pub fn alloc(f: F) -> *mut Link {
unsafe {
let ptr = std::alloc::alloc(Layout::new::<Self>()).cast::<Self>();
ptr.write(DeferredClosure {
f: ManuallyDrop::new(f),
link: Link::new_unique(|ptr: *mut Link| {
let this_ptr = ptr
.map_addr(|addr| addr - offset_of!(Self, link))
.cast::<Self>();
this_ptr.drop_in_place();
dealloc(this_ptr.cast::<u8>(), Layout::new::<Self>());
}),
});
ptr.map_addr(|addr| addr + offset_of!(Self, link))
.cast::<Link>()
}
}
}
impl<F: 'static + FnOnce()> Drop for DeferredClosure<F> {
#[inline]
fn drop(&mut self) {
unsafe {
ManuallyDrop::take(&mut self.f)();
}
}
}