Skip to content

Commit

Permalink
Add iterator for RapIdArena and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
emilevr committed Oct 29, 2023
1 parent 46628d6 commit e0ee72a
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 9 deletions.
4 changes: 3 additions & 1 deletion buildit/src/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl BuildItCommand for CoverageCommand {
// ^\\s*[{}(),;\\[\\] ]*\\s*$ => lines with only delimiters or whitespace, no logic
// ^\\s*impl\\s+[^ ]+\\s*\\{\\s*$ => lines containing only an impl declaration
// ^\\s*impl\\s+[^ ]+\\s+for\\s+[^ ]*\\s*\\{\\s*$ => lines containing only an impl for declaration
// ^\\s*(pub|pub\\s*\\(\\s*crate\\s*\\)\\s*)?\\s*const\\s+.*\\s*[(){}]*\\s*$ => lines containing only a const definition
"^\\s*(debug_)?assert(_eq|_ne)?!\
|^\\s*#\\[.*$\
|^\\s*#!\\[.*$\
Expand All @@ -153,7 +154,8 @@ impl BuildItCommand for CoverageCommand {
|^\\s[});({]*\\s*$\
|^\\s*[{}(),;\\[\\] ]*\\s*$\
|^\\s*impl\\s+[^ ]+\\s*\\{\\s*$\
|^\\s*impl\\s+[^ ]+\\s+for\\s+[^ ]*\\s*\\{\\s*$",
|^\\s*impl\\s+[^ ]+\\s+for\\s+[^ ]*\\s*\\{\\s*$\
|^\\s*(pub|pub\\s*\\(\\s*crate\\s*\\)\\s*)?\\s*const\\s+.*\\s*[(){}]*\\s*$",
"--ignore-not-existing",
"--ignore",
"buildit/*",
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(unreachable_pub)]
//#![forbid(unsafe_code)]

//! Space may be the final frontier, but it sure is annoying when you run out. 🖖
//!
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod test_utils;

mod logging;

pub(crate) const DEFAULT_SIZE_THRESHOLD_PERCENTAGE: u8 = 1;
const DEFAULT_SIZE_THRESHOLD_PERCENTAGE: u8 = 1;

#[derive(Clone, Debug, Parser)]
#[clap(
Expand Down
42 changes: 41 additions & 1 deletion src/rapid_arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<T> RapIdArena<T> {
/// Creates a new arena with each bucket able to hold the specified number of items.
pub fn new_with_bucket_size(items_per_bucket: usize) -> Self {
if items_per_bucket == 0 {
panic!("The specified items per bucket value is invalid! The value must be greater than 0.")
panic!("The specified number of items per bucket is invalid! The value must be greater than 0.")
}
RapIdArena::<T> {
items_per_bucket,
Expand Down Expand Up @@ -94,6 +94,20 @@ impl<T> RapIdArena<T> {
pub fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returs an iterator for the arena contents. This iterator is threadsafe.
pub fn iter(&self) -> RapIdArenaIterator<T> {
let mut data = vec![];
let arena_internals = self.internals.read().unwrap();
for bucket in &arena_internals.buckets {
for item in bucket {
data.push(RapId {
p: ptr::NonNull::from(item),
})
}
}
RapIdArenaIterator::<T> { data, index: 0 }
}
}

impl<T> Default for RapIdArena<T> {
Expand All @@ -108,6 +122,27 @@ unsafe impl<T> Send for RapIdArena<T> {}
// Safety: items_per_bucket is immutable and all the internal values are protected via a RwLock.
unsafe impl<T> Sync for RapIdArena<T> {}

/// An iterator for a RapIdArena instance.
#[derive(Debug)]
pub struct RapIdArenaIterator<T> {
data: Vec<RapId<T>>,
index: usize,
}

impl<T> Iterator for RapIdArenaIterator<T> {
type Item = RapId<T>;

fn next(&mut self) -> Option<Self::Item> {
if self.index < self.data.len() {
let value = self.data[self.index];
self.index += 1;
Some(value)
} else {
None
}
}
}

/// An ID that contains an allocated object.
#[derive(Debug)]
pub struct RapId<T> {
Expand Down Expand Up @@ -138,6 +173,11 @@ impl<T> Deref for RapId<T> {
}

impl<T> DerefMut for RapId<T> {
/// NOTE! If the mutable reference is used concurrently from multiple threads, then T has to be threadsafe
/// or race conditions may occur. Wrap T in Mutex or RwLock rather than storing T instances directly
/// in the arena.
/// However, it is safe to modify instances in a single parallel iterator as each item is accessed
/// only by a single thread at a time.
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
// Safety: The pointer is aligned, initialized, and dereferenceable by the guarantees made by Vec.
Expand Down
35 changes: 31 additions & 4 deletions src/rapid_arena_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;
use criterion::black_box;
use rayon::prelude::{ParallelBridge, ParallelIterator};
use rstest::rstest;
use std::{thread, time::Duration};

Expand Down Expand Up @@ -267,22 +268,22 @@ fn deref_multi_threaded_test() {

#[test]
fn deref_mut_multi_threaded_test() {
let mut arena = RapIdArena::<Something>::new_with_bucket_size(1);
let mut arena = RapIdArena::<RwLock<Something>>::new_with_bucket_size(1);
const ITEM_COUNT: usize = 7;
let mut ids = vec![];
for i in 0..ITEM_COUNT {
ids.push(arena.alloc(Something {
ids.push(arena.alloc(RwLock::new(Something {
some_value: i,
some_string: format!("i = {}", i),
}));
})));
}

std::thread::scope(|s| {
for _ in 0..(ITEM_COUNT * 3) {
s.spawn(|| {
for i in 0..ITEM_COUNT * 11 {
let mut id = ids[i % ITEM_COUNT];
let item = id.deref_mut();
let mut item = id.deref_mut().write().unwrap();

let some_value = item.some_value + 1;
let some_string = format!("i = {}", some_value);
Expand All @@ -299,3 +300,29 @@ fn deref_mut_multi_threaded_test() {
}
});
}

#[test]
fn rayon_test() {
// Arrange
let mut arena = RapIdArena::<Something>::new_with_bucket_size(5);
let count = 31;
alloc_items(&mut arena, count);
// Since we start from 0 not 1, our formula is n(n-1)/2 instead of n(n+1)/2
let expected_sum = count * (count - 1) / 2;

// Act
let sum: usize = arena
.iter()
.par_bridge()
.map(|id| id.deref().some_value)
.sum();

// Assert
assert_eq!(expected_sum, sum);
}

#[test]
#[should_panic]
fn new_with_bucket_size_of_zero_should_panic() {
RapIdArena::<Something>::new_with_bucket_size(0);
}
2 changes: 1 addition & 1 deletion src/space_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod test_utils;

mod cli;

pub(crate) const DEFAULT_SIZE_THRESHOLD_PERCENTAGE: u8 = 1;
const DEFAULT_SIZE_THRESHOLD_PERCENTAGE: u8 = 1;

#[derive(Clone, Debug, Parser)]
#[clap(
Expand Down

0 comments on commit e0ee72a

Please sign in to comment.