#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
extern crate alloc;
use alloc::sync::Arc;
use core::fmt::{self, Debug};
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
trait SaturatingUsize {
fn saturating_increment(&self, order: Ordering) -> usize;
fn saturating_decrement(&self, order: Ordering) -> usize;
}
impl SaturatingUsize for AtomicUsize {
fn saturating_increment(&self, order: Ordering) -> usize {
loop {
let current: usize = self.load(order);
if current == usize::MAX {
return current;
}
let new: usize = current + 1;
match self.compare_exchange(current, new, order, order) {
Ok(_) => return new,
Err(_) => continue, }
}
}
fn saturating_decrement(&self, order: Ordering) -> usize {
loop {
let current: usize = self.load(order);
if current == 0 {
return current;
}
let new: usize = current - 1;
match self.compare_exchange(current, new, order, order) {
Ok(_) => return new,
Err(_) => continue, }
}
}
}
pub trait StealthClone {
fn stealth_clone(&self) -> Self;
}
pub trait AtomicDestroyer: Debug + Clone {
fn on_destroy(&self);
}
pub struct AtomicDestructor<T>
where
T: AtomicDestroyer,
{
destroyed: Arc<AtomicBool>,
counter: Arc<AtomicUsize>,
stealth: bool,
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> 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)
.field("stealth", &self.stealth)
.field("inner", &self.inner)
.finish()
}
}
impl<T> Clone for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn clone(&self) -> Self {
self.counter.saturating_increment(Ordering::SeqCst);
Self {
destroyed: self.destroyed.clone(),
counter: self.counter.clone(),
stealth: false,
inner: self.inner.clone(),
}
}
}
impl<T> StealthClone for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn stealth_clone(&self) -> Self {
Self {
destroyed: self.destroyed.clone(),
counter: self.counter.clone(),
stealth: true,
inner: self.inner.clone(),
}
}
}
impl<T> Drop for AtomicDestructor<T>
where
T: AtomicDestroyer,
{
fn drop(&mut self) {
if self.is_stealth() || self.is_destroyed() {
return;
}
let value: usize = self.counter.saturating_decrement(Ordering::SeqCst);
if value == 0 {
self.inner.on_destroy();
self.destroyed.store(true, Ordering::SeqCst);
}
}
}
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)),
stealth: false,
inner,
}
}
pub fn counter(&self) -> usize {
self.counter.load(Ordering::SeqCst)
}
pub fn is_destroyed(&self) -> bool {
self.destroyed.load(Ordering::SeqCst)
}
pub fn is_stealth(&self) -> bool {
self.stealth
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone)]
struct InternalTestingStealth;
impl AtomicDestroyer for InternalTestingStealth {
fn on_destroy(&self) {}
}
#[derive(Clone)]
struct TestingStealth {
inner: AtomicDestructor<InternalTestingStealth>,
}
impl StealthClone for TestingStealth {
fn stealth_clone(&self) -> Self {
Self {
inner: self.inner.stealth_clone(),
}
}
}
impl TestingStealth {
pub fn new() -> Self {
Self {
inner: AtomicDestructor::new(InternalTestingStealth),
}
}
}
#[test]
fn test_clone() {
let t = TestingStealth::new();
assert_eq!(t.inner.counter(), 1);
assert!(!t.inner.is_stealth());
assert!(!t.inner.is_destroyed());
let t_1 = t.clone();
assert_eq!(t.inner.counter(), 2);
assert_eq!(t_1.inner.counter(), 2);
let t_2 = t_1.clone();
assert_eq!(t.inner.counter(), 3);
assert_eq!(t_1.inner.counter(), 3);
assert_eq!(t_2.inner.counter(), 3);
drop(t_1);
assert_eq!(t.inner.counter(), 2);
assert!(!t.inner.is_destroyed());
drop(t_2);
assert_eq!(t.inner.counter(), 1);
}
#[test]
fn test_stealth_clone() {
let t = TestingStealth::new();
assert_eq!(t.inner.counter(), 1);
assert!(!t.inner.is_stealth());
let t_1 = t.stealth_clone();
assert_eq!(t.inner.counter(), 1);
assert_eq!(t_1.inner.counter(), 1);
assert!(!t.inner.is_stealth());
assert!(t_1.inner.is_stealth());
let t_2 = t_1.clone(); assert_eq!(t.inner.counter(), 2);
assert_eq!(t_1.inner.counter(), 2);
assert_eq!(t_2.inner.counter(), 2);
let t_3 = t.clone(); assert_eq!(t.inner.counter(), 3);
assert_eq!(t_1.inner.counter(), 3);
assert_eq!(t_2.inner.counter(), 3);
drop(t_1); assert_eq!(t.inner.counter(), 3);
drop(t_2); assert_eq!(t.inner.counter(), 2);
drop(t_3); assert_eq!(t.inner.counter(), 1);
}
}