use arc_swap::{ArcSwap, Guard};
use std::sync::Arc;
pub struct CowVec<T> {
inner: ArcSwap<Vec<T>>,
frozen: parking_lot::Mutex<bool>,
}
impl<T> CowVec<T>
where
T: Clone,
{
pub fn new() -> Self {
Self {
inner: ArcSwap::new(Arc::new(Vec::new())),
frozen: parking_lot::Mutex::new(false),
}
}
#[allow(dead_code)]
pub fn from_vec(vec: Vec<T>) -> Self {
Self {
inner: ArcSwap::from(Arc::new(vec)),
frozen: parking_lot::Mutex::new(false),
}
}
pub fn get(&self) -> Guard<Arc<Vec<T>>> {
self.inner.load()
}
pub fn push(&self, item: T) -> bool {
let frozen = self.frozen.lock();
if *frozen {
return false;
}
let current = self.inner.load();
let mut new_vec = Vec::clone(¤t);
new_vec.push(item);
self.inner.store(Arc::new(new_vec));
true
}
pub fn retain<F>(&self, predicate: F)
where
F: FnMut(&T) -> bool,
{
let frozen = self.frozen.lock();
if *frozen {
#[cfg(debug_assertions)]
assert!(self.inner.load().is_empty());
return;
}
let current = self.inner.load();
let mut new_vec = Vec::with_capacity(current.len());
#[allow(clippy::iter_overeager_cloned)]
new_vec.extend(current.iter().cloned().filter(predicate));
self.inner.store(Arc::new(new_vec));
}
pub fn take_and_freeze(&self) -> Arc<Vec<T>> {
let mut frozen = self.frozen.lock();
*frozen = true;
self.inner.swap(Arc::default())
}
}
impl<T> Default for CowVec<T>
where
T: Clone,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::thread;
#[test]
fn test_concurrent_push() {
let vec = Arc::new(CowVec::from_vec(vec![1, 2, 3]));
let vec2 = vec.clone();
let old_snapshot = vec.get();
let handle = thread::spawn(move || {
vec2.push(4);
});
vec.push(5);
handle.join().unwrap();
let final_state = vec.get().to_vec();
assert_eq!(old_snapshot.len(), 3);
assert_eq!(final_state.len(), 5);
}
#[test]
fn test_swap_retain() {
let vec = CowVec::from_vec(vec![1, 2, 3, 4, 5]);
let old_snapshot = vec.get();
vec.retain(|x| x % 2 == 0);
let new_snapshot = vec.get();
assert_eq!(old_snapshot.to_vec(), vec![1, 2, 3, 4, 5]);
assert_eq!(new_snapshot.to_vec(), vec![2, 4]);
let vec = Arc::new(CowVec::from_vec(vec![1, 2, 3, 4, 5]));
let vec2 = vec.clone();
let reading_thread = thread::spawn(move || {
let snapshot = vec2.get();
thread::sleep(std::time::Duration::from_millis(50)); snapshot.to_vec()
});
thread::sleep(std::time::Duration::from_millis(10));
vec.retain(|x| x % 2 == 0);
let read_result = reading_thread.join().unwrap();
assert_eq!(read_result, vec![1, 2, 3, 4, 5]); assert_eq!(vec.get().to_vec(), vec![2, 4]); }
#[test]
fn test_take_and_freeze() {
let orig = vec![1, 2, 3, 4];
let cv = CowVec::from_vec(orig.clone());
assert_eq!(cv.take_and_freeze().as_ref(), &orig);
assert!(cv.get().is_empty());
assert!(!cv.push(5));
cv.retain(|_| true);
assert!(cv.get().is_empty());
}
}