1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright (c) 2024 Yuki Kishimoto
// Distributed under the MIT software license

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 {
    /// Optional name to identify inner in logs/teminal
    #[cfg(feature = "tracing")]
    fn name(&self) -> Option<String> {
        None
    }

    /// Instructions to execute when all instances are dropped
    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 {
        // Increase counter
        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);
        }

        // Clone
        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 {
            // Decrease counter
            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);
            }

            // Check if it's time for destruction
            if value == 0 {
                #[cfg(feature = "tracing")]
                if let Some(name) = &self.inner.name() {
                    tracing::debug!("Destroying {} ...", name);
                }

                // Destroy
                self.inner.on_destroy();

                // Mark as destroyed
                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)
    }
}