use std::cell::RefCell;
use std::sync::Mutex;
pub trait SyncStrategy<T>: Sized {
fn new(inner: T) -> Self;
fn with<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
}
impl<T> SyncStrategy<T> for Mutex<T> {
fn new(inner: T) -> Self {
Mutex::new(inner)
}
fn with<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut guard = match self.lock() {
Ok(g) => g,
Err(e) => {
log::warn!(
"acls: mutex was poisoned (a thread panicked while holding the lock). \
Recovering, but state may be inconsistent."
);
e.into_inner()
}
};
f(&mut guard)
}
}
impl<T> SyncStrategy<T> for RefCell<T> {
fn new(inner: T) -> Self {
RefCell::new(inner)
}
fn with<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
f(&mut self.borrow_mut())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mutex_strategy() {
let cache = Mutex::new(Vec::<String>::new());
cache.with(|data| data.push("test".to_string()));
cache.with(|data| {
assert_eq!(data.len(), 1);
assert_eq!(data[0], "test");
});
}
#[test]
fn test_refcell_strategy() {
let cache = RefCell::new(Vec::<String>::new());
cache.with(|data| data.push("test".to_string()));
cache.with(|data| {
assert_eq!(data.len(), 1);
assert_eq!(data[0], "test");
});
}
#[test]
fn test_generic_function() {
fn count_items<S: SyncStrategy<Vec<i32>>>(storage: &S) -> usize {
storage.with(|data| data.len())
}
let mutex_storage = Mutex::new(vec![1, 2, 3]);
let refcell_storage = RefCell::new(vec![1, 2, 3]);
assert_eq!(count_items(&mutex_storage), 3);
assert_eq!(count_items(&refcell_storage), 3);
}
}