use crate::tls::ThreadLocal;
use alloc::vec::Vec;
use core::{
cell::Cell,
fmt,
marker::PhantomData,
sync::atomic::{AtomicUsize, Ordering::*},
};
#[derive(Debug)]
pub struct Incinerator<T> {
counter: AtomicUsize,
tls_list: ThreadLocal<GarbageList<T>>,
}
impl<T> Incinerator<T> {
pub fn new() -> Self {
Self {
counter: AtomicUsize::new(0),
tls_list: ThreadLocal::new(),
}
}
pub fn pause(&self) -> Pause<'_, T> {
let mut count = self.counter.load(Relaxed);
loop {
if count == usize::MAX {
panic!("Too many pauses");
}
match self
.counter
.compare_exchange(count, count + 1, AcqRel, Relaxed)
{
Ok(_) => {
break Pause {
incin: self,
had_list: self.tls_list.get().is_some(),
_unsync: PhantomData,
};
}
Err(new) => count = new,
}
}
}
pub fn pause_with<F, A>(&self, exec: F) -> A
where
F: FnOnce(&Pause<T>) -> A,
{
let pause = self.pause();
let ret = exec(&pause);
pause.resume();
ret
}
pub fn add(&self, val: T) {
if self.counter.load(Acquire) == 0 {
self.tls_list.get().map(GarbageList::clear);
drop(val);
} else {
self.tls_list.with_init(GarbageList::new).add(val);
}
}
pub fn try_clear(&self) -> bool {
if self.counter.load(Acquire) == 0 {
self.tls_list.get().map(GarbageList::clear);
true
} else {
false
}
}
pub fn clear(&mut self) {
self.tls_list.clear();
}
}
impl<T> Default for Incinerator<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Pause<'incin, T>
where
T: 'incin,
{
incin: &'incin Incinerator<T>,
had_list: bool,
_unsync: PhantomData<*mut ()>,
}
impl<'incin, T> Pause<'incin, T> {
pub fn incin(&self) -> &Incinerator<T> {
self.incin
}
pub fn add_to_incin(&self, val: T) {
if self.incin.counter.load(Acquire) == 1 {
if self.had_list {
self.incin.tls_list.get().map(GarbageList::clear);
}
drop(val);
} else {
self.incin.tls_list.with_init(GarbageList::new).add(val);
}
}
pub fn resume(self) {}
}
impl<'incin, T> Drop for Pause<'incin, T> {
fn drop(&mut self) {
if self.incin.counter.fetch_sub(1, AcqRel) == 1 {
self.incin.tls_list.get().map(GarbageList::clear);
}
}
}
impl<'incin, T> Clone for Pause<'incin, T> {
fn clone(&self) -> Self {
self.incin.pause()
}
}
unsafe impl<'incin, T> Send for Pause<'incin, T> where T: Send {}
struct GarbageList<T> {
list: Cell<Vec<T>>,
}
impl<T> GarbageList<T> {
fn new() -> Self {
Self {
list: Cell::new(Vec::new()),
}
}
fn add(&self, val: T) {
let mut list = self.list.replace(Vec::new());
list.push(val);
self.list.replace(list);
}
fn clear(&self) {
self.list.replace(Vec::new());
}
}
impl<T> fmt::Debug for GarbageList<T>
where
T: fmt::Debug,
{
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
let list = self.list.replace(Vec::new());
write!(fmtr, "{:?}", list)?;
let mut tmp = self.list.replace(list);
if !tmp.is_empty() {
let mut list = self.list.replace(Vec::new());
list.append(&mut tmp);
self.list.replace(list);
}
Ok(())
}
}
macro_rules! doc {
($doc:expr ; $($target:tt)*) => {
#[doc = $doc]
$($target)*
};
}
macro_rules! make_shared_incin {
(
{ $target:expr }
$(#[$meta:meta])*
$vis:vis $name:ident<$($params:ident),*> of $garbage:ty
) => {
doc! {
concat!("The shared incinerator used by ", $target, ". You may \
want to use this type in order to reduce memory \
consumption of the minimal space required by the \
incinerator. However, garbage items may be hold for \
longer time than they would if no shared incinerator \
were used.");
$(#[$meta])*
$vis struct $name<$($params),*> {
inner: core::mem::MaybeUninit<alloc::sync::Arc<crate::incin::Incinerator<$garbage>>>,
}
}
impl<$($params),*> $name<$($params),*> {
fn get_unchecked(&self) -> &alloc::sync::Arc<crate::incin::Incinerator<$garbage>> {
unsafe{
self.inner.assume_init_ref()
}
}
doc! {
concat!("Creates a new shared incinerator for ", $target, ".");
$vis fn new() -> Self {
use crate::incin::Incinerator;
use core::mem::MaybeUninit;
Self {
inner: MaybeUninit::new(alloc::sync::Arc::new(Incinerator::new())),
}
}
}
doc! {
concat!("Tries to clear the incinerator garbage list in the \
best possible way given the runtime status of this \
incinerator.");
$vis fn clear(&mut self) {
use alloc::sync::Arc;
let arc = unsafe {
self.inner.assume_init_read()
};
match Arc::try_unwrap(arc) {
Ok(mut incin) => {
incin.clear();
self.inner.write(Arc::new(incin));
},
Err(arc) => {
arc.try_clear();
self.inner.write(arc);
}
}
}
}
}
impl<$($params),*> Default for $name<$($params),*> {
fn default() -> Self {
Self::new()
}
}
impl<$($params),*> Clone for $name<$($params),*> {
fn clone(&self) -> Self {
let inner =unsafe{
core::mem::MaybeUninit::new(self.inner.assume_init_ref().clone())
};
Self {
inner
}
}
}
};
}