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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
#![doc = include_str!("../README.md")]
#![doc(html_root_url = "https://docs.rs/reference-counted-singleton/0.1.4")]
#![warn(unsafe_op_in_unsafe_fn)]
#[cfg(test)]
mod tests;
use std::error::Error;
use std::hash::{Hash, Hasher};
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::sync::{Arc, Mutex};
/// A reference-counted singleton whose protected data can be recreated
/// as needed.
///
/// The protected data is created when [`RefCountedSingleton::get_or_init`]
/// is called.
/// That function returns an [`RCSRef`] reference to the singleton.
///
/// [`RCSRef`] instances can be cloned as needed.
/// The last [`RCSRef`] reference drops the data.
/// Calling [`RefCountedSingleton::get_or_init`] again recreates the data.
#[derive(Debug)]
pub struct RefCountedSingleton<T>(Mutex<Option<Arc<T>>>);
impl<T> Default for RefCountedSingleton<T> {
fn default() -> Self {
Self(Mutex::new(None))
}
}
impl<T> RefCountedSingleton<T> {
/// Return a counted reference to the protected data if such data exists,
/// otherwise creates a new instance of the data by calling `creator()`.
///
/// If the lock is poisoned, then this returns `Err(None)`.
/// If `creator()` returns an error `err`, then this returns
/// `Err(Some(err))`.
pub fn get_or_init<E: Error>(
&'_ self,
creator: impl FnOnce() -> Result<T, E>,
) -> Result<RCSRef<'_, T>, Option<E>> {
if let Ok(mut value) = self.0.lock() {
match *value {
// Data is not created.
None => match creator() {
Ok(data) => {
// We created a new instance.
let data = Arc::new(data);
let data_ref = Arc::clone(&data);
*value = Some(data);
Ok(RCSRef {
data: ManuallyDrop::new(data_ref),
rcs: self,
})
}
// Failed to create a new instance of the data.
Err(err) => Err(Some(err)),
},
// Data is already created. Return a new reference.
Some(ref data) => Ok(RCSRef {
data: ManuallyDrop::new(Arc::clone(data)),
rcs: self,
}),
}
} else {
Err(None) // The mutex was poisoned.
}
}
/// Return a counted reference to the protected data if such data exists.
///
/// If such data is not instantiated, or the lock is poisoned, then this
/// returns `None`.
pub fn get(&'_ self) -> Option<RCSRef<'_, T>> {
self.0.lock().ok().and_then(|value| {
value.as_ref().map(|data| RCSRef {
data: ManuallyDrop::new(Arc::clone(data)),
rcs: self,
})
})
}
}
/// Read-only counted reference to an instance of [`RefCountedSingleton`].
#[derive(Debug)]
pub struct RCSRef<'t, T> {
data: ManuallyDrop<Arc<T>>,
rcs: &'t RefCountedSingleton<T>,
}
impl<'t, T: PartialEq> PartialEq for RCSRef<'t, T> {
fn eq(&self, other: &Self) -> bool {
Arc::as_ref(&self.data).eq(Arc::as_ref(&other.data))
}
}
impl<'t, T: Eq> Eq for RCSRef<'t, T> {}
impl<'t, T: PartialOrd> PartialOrd for RCSRef<'t, T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Arc::as_ref(&self.data).partial_cmp(Arc::as_ref(&other.data))
}
}
impl<'t, T: Ord> Ord for RCSRef<'t, T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
Arc::as_ref(&self.data).cmp(Arc::as_ref(&other.data))
}
}
impl<'t, T: Hash> Hash for RCSRef<'t, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ref(&self.data).hash(state)
}
}
impl<'t, T> Deref for RCSRef<'t, T> {
type Target = T;
fn deref(&self) -> &T {
Arc::as_ref(&self.data)
}
}
impl<'t, T> Clone for RCSRef<'t, T> {
fn clone(&self) -> Self {
Self {
data: ManuallyDrop::new(Arc::clone(&self.data)),
rcs: self.rcs,
}
}
}
impl<'t, T> Drop for RCSRef<'t, T> {
fn drop(&mut self) {
// Drop our own counted reference.
// SAFETY: `self.data` is not used after this.
unsafe { ManuallyDrop::drop(&mut self.data) };
if let Ok(mut value) = self.rcs.0.lock() {
if let Some(data) = value.take() {
match Arc::try_unwrap(data) {
// Singleton locked, and there are no more counted references to it.
// Destroy the singleton.
Ok(data) => drop(data),
// Singleton locked, but there are other counted references to it.
// Put the singleton data back.
Err(data) => *value = Some(data),
}
}
}
}
}