use std::{
cell::Cell,
fmt,
marker::PhantomData,
sync::atomic::{AtomicUsize, Ordering::*},
};
use tls::ThreadLocal;
#[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_value() {
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.len() > 0 {
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: ::std::sync::Arc<::incin::Incinerator<$garbage>>,
}
}
impl<$($params),*> $name<$($params),*> {
doc! {
concat!("Creates a new shared incinerator for ", $target, ".");
$vis fn new() -> Self {
use std::sync::Arc;
use incin::Incinerator;
Self {
inner: 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 std::{
mem::{forget, replace, uninitialized},
sync::Arc,
};
let arc = unsafe {
replace(&mut self.inner, uninitialized())
};
match Arc::try_unwrap(arc) {
Ok(mut incin) => {
incin.clear();
forget(replace(&mut self.inner, Arc::new(incin)));
},
Err(arc) => {
arc.try_clear();
forget(replace(&mut self.inner, arc));
}
}
}
}
}
impl<$($params),*> Default for $name<$($params),*> {
fn default() -> Self {
Self::new()
}
}
impl<$($params),*> Clone for $name<$($params),*> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
};
}