extern crate alloc;
use alloc::sync::Arc;
use core::fmt::{self, Debug};
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
mod saturating;
use self::saturating::SaturatingUsize;
pub trait AtomicDestroyer: Debug + Clone {
#[cfg(feature = "tracing")]
fn name(&self) -> Option<String> {
None
}
fn on_destroy(&self);
}
pub struct AtomicDestructor<T>
where
T: AtomicDestroyer,
{
destroyed: Arc<AtomicBool>,
counter: Arc<AtomicUsize>,
inner: T,
}
impl<T> Deref for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T> fmt::Debug for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AtomicDestructor")
.field("destroyed", &self.destroyed)
.field("counter", &self.counter)
.finish()
}
}
impl<T> Clone for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn clone(&self) -> Self {
let _value: usize = self.counter.saturating_increment(Ordering::SeqCst);
#[cfg(feature = "tracing")]
if let Some(name) = &self.inner.name() {
tracing::debug!("{} cloned: atomic counter increased to {}", name, _value);
}
Self {
destroyed: self.destroyed.clone(),
counter: self.counter.clone(),
inner: self.inner.clone(),
}
}
}
impl<T> Drop for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn drop(&mut self) {
if self.is_destroyed() {
#[cfg(feature = "tracing")]
if let Some(name) = &self.inner.name() {
tracing::debug!("{} already destroyed.", name);
}
} else {
let value: usize = self.counter.saturating_decrement(Ordering::SeqCst);
#[cfg(feature = "tracing")]
if let Some(name) = &self.inner.name() {
tracing::debug!("{} dropped: atomic counter decreased to {}", name, value);
}
if value == 0 {
#[cfg(feature = "tracing")]
if let Some(name) = &self.inner.name() {
tracing::debug!("Destroying {} ...", name);
}
self.inner.on_destroy();
self.destroyed.store(true, Ordering::SeqCst);
#[cfg(feature = "tracing")]
if let Some(name) = &self.inner.name() {
tracing::debug!("{} destroyed", name);
}
}
}
}
}
impl<T> AtomicDestructor<T>
where
T: AtomicDestroyer,
{
pub fn new(inner: T) -> Self {
Self {
destroyed: Arc::new(AtomicBool::new(false)),
counter: Arc::new(AtomicUsize::new(1)),
inner,
}
}
pub fn is_destroyed(&self) -> bool {
self.destroyed.load(Ordering::SeqCst)
}
}