drop_bin/
lib.rs

1//! In Rust, values' destructors are automatically run when they go out of scope. However,
2//! destructors can be expensive and so you may wish to defer running them until later, when your
3//! program has some free time or memory usage is getting high. A bin allows you to put any number
4//! of differently-typed values in it, and you can clear them all out, running their destructors,
5//! whenever you want.
6//!
7//! # Example
8//!
9//! ```
10//! let bin = drop_bin::Bin::new();
11//!
12//! let some_data = "Hello World!".to_owned();
13//! bin.add(some_data);
14//! // `some_data`'s destructor is not run.
15//!
16//! bin.clear();
17//! // `some_data`'s destructor has been run.
18//! ```
19#![warn(
20    clippy::pedantic,
21    rust_2018_idioms,
22    missing_docs,
23    unused_qualifications,
24    unsafe_op_in_unsafe_fn
25)]
26
27use std::sync::atomic;
28use std::sync::atomic::AtomicBool;
29use try_rwlock::TryRwLock;
30
31mod concurrent_list;
32use concurrent_list::ConcurrentList;
33
34mod concurrent_slice;
35use concurrent_slice::ConcurrentSlice;
36
37mod concurrent_vec;
38use concurrent_vec::ConcurrentVec;
39
40mod inner;
41use inner::Inner;
42
43/// A container that holds values for later destruction.
44///
45/// It is automatically cleared when it is dropped.
46#[derive(Debug, Default)]
47pub struct Bin<'a> {
48    /// The inner data of the bin. If this is locked for writing, the bin is being cleared.
49    inner: TryRwLock<Inner<'a>>,
50    /// Whether the bin needs to be cleared.
51    clear: AtomicBool,
52}
53
54impl<'a> Bin<'a> {
55    /// Create a new bin.
56    #[must_use]
57    pub const fn new() -> Self {
58        Self {
59            inner: TryRwLock::new(Inner::new()),
60            clear: AtomicBool::new(false),
61        }
62    }
63
64    /// Add a value to the bin.
65    ///
66    /// This may drop the value immediately, but will attempt to store it so that it can be dropped
67    /// later.
68    pub fn add<T: Send + 'a>(&self, value: T) {
69        if let Some(inner) = self.inner.try_read() {
70            inner.add(value);
71        } else {
72            // Just drop the value if the bin is being cleared.
73        }
74
75        self.try_clear();
76    }
77
78    /// Clear the bin, dropping all values that have been previously added to it.
79    ///
80    /// This may not clear the bin immediately if another thread is currently adding a value to the
81    /// bin.
82    pub fn clear(&self) {
83        self.clear.store(true, atomic::Ordering::Relaxed);
84
85        self.try_clear();
86    }
87
88    /// Attempt to the clear the bin.
89    fn try_clear(&self) {
90        if self.clear.load(atomic::Ordering::Relaxed) {
91            if let Some(mut inner) = self.inner.try_write() {
92                self.clear.store(false, atomic::Ordering::Relaxed);
93                inner.clear();
94            }
95        }
96    }
97
98    /// Get the size of the bin in bytes.
99    #[must_use]
100    pub fn size(&self) -> usize {
101        self.inner.try_read().map_or(0, |inner| inner.size())
102    }
103}
104
105impl<'a> Drop for Bin<'a> {
106    fn drop(&mut self) {
107        self.inner.get_mut().clear();
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use crate::test_util::assert_thread_safe;
114    use crate::test_util::CallOnDrop;
115    use crate::Bin;
116    use std::sync::atomic::AtomicBool;
117    use std::sync::atomic::Ordering::SeqCst;
118
119    #[test]
120    fn clear() {
121        let destructor_called = AtomicBool::new(false);
122
123        let bin = Bin::new();
124
125        bin.add(CallOnDrop(
126            || assert!(!destructor_called.swap(true, SeqCst)),
127        ));
128        assert!(!destructor_called.load(SeqCst));
129
130        bin.clear();
131        assert!(destructor_called.load(SeqCst));
132    }
133
134    #[test]
135    #[allow(clippy::extra_unused_lifetimes)]
136    fn thread_safe<'a>() {
137        assert_thread_safe::<Bin<'a>>();
138    }
139}
140
141#[cfg(test)]
142mod test_util {
143    pub(crate) fn assert_thread_safe<T: Send + Sync>() {}
144
145    pub(crate) struct CallOnDrop<T: FnMut()>(pub(crate) T);
146    impl<T: FnMut()> Drop for CallOnDrop<T> {
147        fn drop(&mut self) {
148            self.0();
149        }
150    }
151}