Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement clone for LockFreeFrozenVec #57

Merged
merged 10 commits into from
Oct 24, 2023
13 changes: 13 additions & 0 deletions src/index_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,16 @@ impl<K: Eq + Hash, V, S: Default> Default for FrozenIndexMap<K, V, S> {
}
}
}

impl<K: Clone, V: Clone, S: Clone> Clone for FrozenIndexMap<K, V, S> {
fn clone(&self) -> Self {
assert!(!self.in_use.get());
self.in_use.set(true);
let self_clone = Self {
map: unsafe { self.map.get().as_ref().unwrap() }.clone().into(),
in_use: Cell::from(false),
};
self.in_use.set(false);
return self_clone;
}
}
13 changes: 13 additions & 0 deletions src/index_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,16 @@ impl<T: Eq + Hash, S: Default> Default for FrozenIndexSet<T, S> {
Self::from(IndexSet::default())
}
}

impl<K: Clone, V: Clone> Clone for FrozenIndexSet<K, V> {
fn clone(&self) -> Self {
assert!(!self.in_use.get());
self.in_use.set(true);
let self_clone = Self {
set: unsafe { self.set.get().as_ref().unwrap() }.clone().into(),
in_use: Cell::from(false),
};
self.in_use.set(false);
return self_clone;
}
}
26 changes: 26 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,19 @@ impl<K: Eq + Hash, V, S: Default> Default for FrozenMap<K, V, S> {
}
}

impl<K: Clone, V: Clone, S: Clone> Clone for FrozenMap<K, V, S> {
fn clone(&self) -> Self {
assert!(!self.in_use.get());
self.in_use.set(true);
let self_clone = Self {
map: unsafe { self.map.get().as_ref().unwrap() }.clone().into(),
in_use: Cell::from(false),
};
self.in_use.set(false);
return self_clone;
}
}

/// Append-only version of `std::collections::BTreeMap` where
/// insertion does not require mutable access
pub struct FrozenBTreeMap<K, V> {
Expand Down Expand Up @@ -495,3 +508,16 @@ impl<K: Clone + Ord, V: StableDeref> Default for FrozenBTreeMap<K, V> {
}
}
}

impl<K: Clone, V: Clone> Clone for FrozenBTreeMap<K, V> {
fn clone(&self) -> Self {
assert!(!self.in_use.get());
self.in_use.set(true);
let self_clone = Self {
map: unsafe { self.map.get().as_ref().unwrap() }.clone().into(),
in_use: Cell::from(false),
};
self.in_use.set(false);
return self_clone;
}
}
87 changes: 87 additions & 0 deletions src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,14 @@ impl<K, V> std::convert::AsMut<HashMap<K, V>> for FrozenMap<K, V> {
}
}

impl<K: Clone, V: Clone> Clone for FrozenMap<K, V> {
fn clone(&self) -> Self {
Self {
map: self.map.read().unwrap().clone().into(),
}
}
}

/// Append-only threadsafe version of `std::vec::Vec` where
/// insertion does not require mutable access
pub struct FrozenVec<T> {
Expand Down Expand Up @@ -488,6 +496,14 @@ impl<T> Default for FrozenVec<T> {
}
}

impl<T: Clone> Clone for FrozenVec<T> {
fn clone(&self) -> Self {
Self {
vec: self.vec.read().unwrap().clone().into(),
}
}
}

// The context for these functions is that we want to have a
// series of exponentially increasing buffer sizes. We want
// to maximize the total size of the buffers (since this
Expand Down Expand Up @@ -677,6 +693,40 @@ impl<T: Copy> LockFreeFrozenVec<T> {
let local_index = index - prior_total_buffer_size(buffer_idx);
unsafe { *buffer_ptr.add(local_index) }
}

/// Run a function on each buffer in the vector.
///
/// ## Arguments
/// - `func`: a function that takes a pointer to the buffer, the buffer size, and the buffer index
///
/// ## Safety
/// `func` is provided with the raw constant pointer to the buffer,
/// however, the pointer is expected to be valid as the null check is done before calling `func`.
/// To access the data in the buffer, make sure the buffer size (the second argument) is respected.
aminya marked this conversation as resolved.
Show resolved Hide resolved
///
pub unsafe fn for_each_buffer(&self, mut func: impl FnMut(*const T, usize, usize)) {
aminya marked this conversation as resolved.
Show resolved Hide resolved
aminya marked this conversation as resolved.
Show resolved Hide resolved
let len = self.len.load(Ordering::Acquire);
// handle the empty case
if len == 0 {
return;
}
aminya marked this conversation as resolved.
Show resolved Hide resolved

// for each buffer, run the function
for buffer_index in 0..NUM_BUFFERS {
// get the buffer pointer
let buffer_ptr = self.data[buffer_index].load(Ordering::Acquire);
if buffer_ptr.is_null() {
// no data in this buffer, so we're done
break;
}

// get the buffer size and index
let buffer_size = buffer_size(buffer_index);

// run the function
func(buffer_ptr, buffer_size, buffer_index);
}
}
}

#[test]
Expand Down Expand Up @@ -713,6 +763,31 @@ fn test_non_lockfree_unchecked() {
LockFreeFrozenVec::<()>::new();
}

impl<T: Copy + Clone> Clone for LockFreeFrozenVec<T> {
aminya marked this conversation as resolved.
Show resolved Hide resolved
fn clone(&self) -> Self {
let mut coppied_data = [Self::NULL; NUM_BUFFERS];
// for each buffer, copy the data
unsafe {
self.for_each_buffer(|buffer_ptr, buffer_size, buffer_index| {
// allocate a new buffer
let layout = Self::layout(buffer_size);
let new_buffer_ptr = std::alloc::alloc(layout).cast::<T>();
assert!(!new_buffer_ptr.is_null());
// copy the data
std::ptr::copy_nonoverlapping(buffer_ptr, new_buffer_ptr, buffer_size);
// store the new buffer pointer
*coppied_data[buffer_index].get_mut() = new_buffer_ptr;
})
};

return Self {
data: coppied_data,
len: AtomicUsize::new(self.len.load(Ordering::Relaxed)),
locked: AtomicBool::new(false),
};
}
}

#[test]
fn test_non_lockfree() {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -744,6 +819,12 @@ fn test_non_lockfree() {
}
});

// Test cloning
let vec2 = vec.clone();
assert_eq!(vec2.get(0), Some(Moo(1)));
assert_eq!(vec2.get(1), Some(Moo(2)));
assert_eq!(vec2.get(2), Some(Moo(3)));

// Test dropping empty vecs
LockFreeFrozenVec::<()>::new();
}
Expand Down Expand Up @@ -933,3 +1014,9 @@ impl<K: Clone + Ord, V: StableDeref> Default for FrozenBTreeMap<K, V> {
Self::new()
}
}

impl<K: Clone, V: Clone> Clone for FrozenBTreeMap<K, V> {
fn clone(&self) -> Self {
Self(self.0.read().unwrap().clone().into())
}
}
8 changes: 8 additions & 0 deletions src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ impl<T> Default for FrozenVec<T> {
}
}

impl<T: Clone> Clone for FrozenVec<T> {
fn clone(&self) -> Self {
Self {
vec: unsafe { self.vec.get().as_ref().unwrap() }.clone().into(),
}
}
}

impl<T> From<Vec<T>> for FrozenVec<T> {
fn from(vec: Vec<T>) -> Self {
Self {
Expand Down
Loading