reference_counted_singleton/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(html_root_url = "https://docs.rs/reference-counted-singleton/0.1.5")]
3#![warn(unsafe_op_in_unsafe_fn)]
4
5#[cfg(test)]
6mod tests;
7
8use std::error::Error;
9use std::hash::{Hash, Hasher};
10use std::mem::ManuallyDrop;
11use std::ops::Deref;
12use std::sync::{Arc, Mutex};
13
14/// A reference-counted singleton whose protected data can be recreated
15/// as needed.
16///
17/// The protected data is created when [`RefCountedSingleton::get_or_init`]
18/// is called.
19/// That function returns an [`RCSRef`] reference to the singleton.
20///
21/// [`RCSRef`] instances can be cloned as needed.
22/// The last [`RCSRef`] reference drops the data.
23/// Calling [`RefCountedSingleton::get_or_init`] again recreates the data.
24#[derive(Debug)]
25pub struct RefCountedSingleton<T>(Mutex<Option<Arc<T>>>);
26
27impl<T> Default for RefCountedSingleton<T> {
28    fn default() -> Self {
29        Self(Mutex::new(None))
30    }
31}
32
33impl<T> RefCountedSingleton<T> {
34    /// Return a counted reference to the protected data if such data exists,
35    /// otherwise creates a new instance of the data by calling `creator()`.
36    ///
37    /// If the lock is poisoned, then this returns `Err(None)`.
38    /// If `creator()` returns an error `err`, then this returns
39    /// `Err(Some(err))`.
40    pub fn get_or_init<E: Error>(
41        &'_ self,
42        creator: impl FnOnce() -> Result<T, E>,
43    ) -> Result<RCSRef<'_, T>, Option<E>> {
44        if let Ok(mut value) = self.0.lock() {
45            match *value {
46                // Data is not created.
47                None => match creator() {
48                    Ok(data) => {
49                        // We created a new instance.
50                        let data = Arc::new(data);
51                        let data_ref = Arc::clone(&data);
52
53                        *value = Some(data);
54
55                        Ok(RCSRef {
56                            data: ManuallyDrop::new(data_ref),
57                            rcs: self,
58                        })
59                    }
60
61                    // Failed to create a new instance of the data.
62                    Err(err) => Err(Some(err)),
63                },
64
65                // Data is already created. Return a new reference.
66                Some(ref data) => Ok(RCSRef {
67                    data: ManuallyDrop::new(Arc::clone(data)),
68                    rcs: self,
69                }),
70            }
71        } else {
72            Err(None) // The mutex was poisoned.
73        }
74    }
75
76    /// Return a counted reference to the protected data if such data exists.
77    ///
78    /// If such data is not instantiated, or the lock is poisoned, then this
79    /// returns `None`.
80    pub fn get(&'_ self) -> Option<RCSRef<'_, T>> {
81        self.0.lock().ok().and_then(|value| {
82            value.as_ref().map(|data| RCSRef {
83                data: ManuallyDrop::new(Arc::clone(data)),
84                rcs: self,
85            })
86        })
87    }
88}
89
90/// Read-only counted reference to an instance of [`RefCountedSingleton`].
91#[derive(Debug)]
92pub struct RCSRef<'t, T> {
93    data: ManuallyDrop<Arc<T>>,
94    rcs: &'t RefCountedSingleton<T>,
95}
96
97impl<'t, T: PartialEq> PartialEq for RCSRef<'t, T> {
98    fn eq(&self, other: &Self) -> bool {
99        Arc::as_ref(&self.data).eq(Arc::as_ref(&other.data))
100    }
101}
102
103impl<'t, T: Eq> Eq for RCSRef<'t, T> {}
104
105impl<'t, T: PartialOrd> PartialOrd for RCSRef<'t, T> {
106    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
107        Arc::as_ref(&self.data).partial_cmp(Arc::as_ref(&other.data))
108    }
109}
110
111impl<'t, T: Ord> Ord for RCSRef<'t, T> {
112    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
113        Arc::as_ref(&self.data).cmp(Arc::as_ref(&other.data))
114    }
115}
116
117impl<'t, T: Hash> Hash for RCSRef<'t, T> {
118    fn hash<H: Hasher>(&self, state: &mut H) {
119        Arc::as_ref(&self.data).hash(state)
120    }
121}
122
123impl<'t, T> Deref for RCSRef<'t, T> {
124    type Target = T;
125
126    fn deref(&self) -> &T {
127        Arc::as_ref(&self.data)
128    }
129}
130
131impl<'t, T> Clone for RCSRef<'t, T> {
132    fn clone(&self) -> Self {
133        Self {
134            data: ManuallyDrop::new(Arc::clone(&self.data)),
135            rcs: self.rcs,
136        }
137    }
138}
139
140impl<'t, T> Drop for RCSRef<'t, T> {
141    fn drop(&mut self) {
142        // Drop our own counted reference.
143        // SAFETY: `self.data` is not used after this.
144        unsafe { ManuallyDrop::drop(&mut self.data) };
145
146        if let Ok(mut value) = self.rcs.0.lock() {
147            if let Some(data) = value.take() {
148                match Arc::try_unwrap(data) {
149                    // Singleton locked, and there are no more counted references to it.
150                    // Destroy the singleton.
151                    Ok(data) => drop(data),
152
153                    // Singleton locked, but there are other counted references to it.
154                    // Put the singleton data back.
155                    Err(data) => *value = Some(data),
156                }
157            }
158        }
159    }
160}