use std::sync::Arc;
#[cfg(not(target_family = "wasm"))]
use foundation_nostd::comp::basic::RwLock;
#[cfg(target_family = "wasm")]
pub use foundation_nostd::primitives::RwLock;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Entry {
pub id: usize,
pub gen: usize,
}
#[allow(dead_code)]
impl Entry {
#[must_use]
pub fn new(id: usize, gen: usize) -> Self {
Self { id, gen }
}
}
#[derive(Debug, Clone)]
pub struct EntryList<T> {
items: Vec<(usize, Option<T>)>,
free_entries: Vec<Entry>,
packed_entries: Vec<Entry>,
}
impl<T> EntryList<T> {
#[must_use]
pub fn new() -> Self {
Self {
items: Vec::new(),
free_entries: Vec::new(),
packed_entries: Vec::new(),
}
}
}
impl<T> Default for EntryList<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> EntryList<T> {
#[inline]
#[must_use]
pub fn active_slots(&self) -> usize {
self.allocated_slots() - self.open_slots()
}
#[inline]
#[must_use]
pub fn parked_slots(&self) -> usize {
self.packed_entries.len()
}
#[inline]
#[must_use]
pub fn allocated_slots(&self) -> usize {
self.items.len()
}
#[inline]
#[must_use]
pub fn open_slots(&self) -> usize {
self.free_entries.len()
}
#[inline]
pub fn get_mut(&mut self, entry: &Entry) -> Option<&mut T> {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen && value.is_some() {
return value.as_mut();
}
}
None
}
#[inline]
#[must_use]
pub fn get(&self, entry: &Entry) -> Option<&T> {
if let Some((gen, value)) = self.items.get(entry.id) {
if *gen == entry.gen && value.is_some() {
return value.as_ref();
}
}
None
}
#[inline]
pub fn not_valid(&mut self, entry: &Entry) -> bool {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen != entry.gen {
return true;
}
if *gen == entry.gen && value.is_none() {
return true;
}
}
false
}
#[inline]
pub fn has(&mut self, entry: &Entry) -> bool {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen && value.is_some() {
return true;
}
}
false
}
#[inline]
pub fn vacate(&mut self, entry: &Entry) {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen {
if let Some(con) = value.take() {
self.free_entries.push(*entry);
drop(con);
}
}
}
}
#[inline]
pub fn clear(&mut self) {
self.items.clear();
self.free_entries.clear();
self.packed_entries.clear();
}
#[inline]
pub fn park(&mut self, entry: &Entry) -> Option<T> {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen {
if let Some(con) = value.take() {
self.packed_entries.push(*entry);
return Some(con);
}
}
}
None
}
pub fn unpark(&mut self, entry: &Entry, item: T) -> bool {
match self.find_packed(entry) {
Some(index) => {
self.packed_entries.remove(index);
let _ = self.update_packed(entry, item);
true
}
None => false,
}
}
#[must_use]
pub fn find_packed(&self, entry: &Entry) -> Option<usize> {
for (index, item) in self.packed_entries.iter().enumerate() {
if item == entry {
return Some(index);
}
}
None
}
#[inline]
pub fn update_packed(&mut self, entry: &Entry, item: T) -> Option<T> {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen && value.is_none() {
let previous_value = value.take();
*value = Some(item);
return previous_value;
}
}
None
}
#[inline]
pub fn take(&mut self, entry: &Entry) -> Option<T> {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen {
if let Some(con) = value.take() {
self.free_entries.push(*entry);
return Some(con);
}
}
}
None
}
#[inline]
pub fn update(&mut self, entry: &Entry, item: T) -> Option<T> {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen && value.is_some() {
let previous_value = value.take();
*value = Some(item);
return previous_value;
}
}
None
}
#[inline]
pub fn map_with<V>(&self, tn: impl Fn(&T) -> Option<V>) -> Vec<V> {
self.items
.iter()
.filter_map(|(_gen, value)| -> Option<V> {
if value.is_none() {
return None;
}
match value {
Some(item) => tn(item),
None => None,
}
})
.collect()
}
#[inline]
pub fn for_each(&self, tn: impl Fn(Option<&T>)) {
self.items.iter().for_each(|(_gen, value)| {
if value.is_none() {
return;
}
tn(value.as_ref());
});
}
#[inline]
pub fn select_take(&mut self, tn: impl Fn(&T) -> bool) -> Vec<T> {
self.items
.iter_mut()
.enumerate()
.filter(|(_index, (_gen, value))| match value {
Some(inner) => tn(inner),
None => false,
})
.map(|(index, (gen, value))| {
self.free_entries.push(Entry::new(index, *gen));
value.take().unwrap()
})
.collect()
}
#[inline]
pub fn replace(&mut self, entry: &Entry, item: T) -> Option<(Entry, T)> {
if let Some((gen, value)) = self.items.get_mut(entry.id) {
if *gen == entry.gen {
let new_gen = *gen + 1;
if value.is_some() {
let previous_value = value.take();
tracing::debug!(
"Current entry with gen: {} going to new gen: {} for id: {}",
gen,
new_gen,
entry.id,
);
*gen = new_gen;
tracing::debug!("Update gen to: {} for id: {}", new_gen, entry.id);
*value = Some(item);
return Some((
Entry {
gen: new_gen,
id: entry.id,
},
previous_value.unwrap(),
));
}
}
}
None
}
#[inline]
pub fn insert(&mut self, item: T) -> Entry {
let entry = match self.free_entries.pop() {
Some(mut inner) => {
inner.gen += 1;
inner
}
None => Entry {
id: self.items.len(),
gen: 0,
},
};
if self.items.len() == entry.id {
self.items.push((entry.gen, Some(item)));
} else {
self.items[entry.id] = (entry.gen, Some(item));
}
entry
}
}
pub struct ThreadSafeEntry<T>(Arc<RwLock<EntryList<T>>>);
impl<T> Default for ThreadSafeEntry<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> ThreadSafeEntry<T> {
#[must_use]
pub fn new() -> Self {
Self(Arc::new(RwLock::new(EntryList::new())))
}
#[must_use]
pub fn from(list: EntryList<T>) -> Self {
Self(Arc::new(RwLock::new(list)))
}
}
impl<T> Clone for ThreadSafeEntry<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
#[allow(unused)]
impl<T> ThreadSafeEntry<T> {
#[inline]
#[must_use]
pub fn active_slots(&self) -> usize {
self.0.read().unwrap().active_slots()
}
#[inline]
#[must_use]
pub fn parked_slots(&self) -> usize {
self.0.read().unwrap().parked_slots()
}
#[inline]
#[must_use]
pub fn allocated_slots(&self) -> usize {
self.0.read().unwrap().allocated_slots()
}
#[inline]
#[must_use]
pub fn open_slots(&self) -> usize {
self.0.read().unwrap().open_slots()
}
#[inline]
pub fn get_mut<Func: FnOnce(Option<&mut T>)>(&self, entry: &Entry, fnc: Func) {
let mut handle = self.0.write().unwrap();
fnc(handle.get_mut(entry));
}
#[inline]
pub fn get<Func: FnOnce(Option<&T>)>(&self, entry: &Entry, fnc: Func) {
let handle = self.0.read().unwrap();
fnc(handle.get(entry));
}
#[inline]
#[must_use]
pub fn not_valid(&self, entry: &Entry) -> bool {
self.0.write().unwrap().not_valid(entry)
}
#[inline]
#[must_use]
pub fn has(&self, entry: &Entry) -> bool {
self.0.write().unwrap().has(entry)
}
#[inline]
pub fn vacate(&self, entry: &Entry) {
self.0.write().unwrap().vacate(entry);
}
#[inline]
#[must_use]
pub fn park(&self, entry: &Entry) -> Option<T> {
self.0.write().unwrap().park(entry)
}
pub fn unpark(&self, entry: &Entry, item: T) -> bool {
self.0.write().unwrap().unpark(entry, item)
}
#[must_use]
pub fn find_packed(&self, entry: &Entry) -> Option<usize> {
self.0.write().unwrap().find_packed(entry)
}
#[inline]
pub fn update_packed(&self, entry: &Entry, item: T) -> Option<T> {
self.0.write().unwrap().update_packed(entry, item)
}
#[inline]
#[must_use]
pub fn take(&self, entry: &Entry) -> Option<T> {
self.0.write().unwrap().take(entry)
}
#[inline]
pub fn update(&self, entry: &Entry, item: T) -> Option<T> {
self.0.write().unwrap().update(entry, item)
}
#[inline]
pub fn map_with<V>(&self, tn: impl Fn(&T) -> Option<V>) -> Vec<V> {
self.0.read().unwrap().map_with(tn)
}
#[inline]
pub fn for_each(&self, tn: impl Fn(Option<&T>)) {
self.0.read().unwrap().for_each(tn);
}
#[inline]
pub fn select_take(&self, tn: impl Fn(&T) -> bool) -> Vec<T> {
self.0.write().unwrap().select_take(tn)
}
#[inline]
pub fn replace(&self, entry: &Entry, item: T) -> Option<(Entry, T)> {
self.0.write().unwrap().replace(entry, item)
}
#[inline]
pub fn insert(&self, item: T) -> Entry {
self.0.write().unwrap().insert(item)
}
}
#[cfg(all(test, not(target_family = "wasm")))]
mod test_entry_list {
use tracing_test::traced_test;
use super::*;
#[test]
fn entry_list_insert_reference() {
let mut list: EntryList<&usize> = EntryList::new();
let entry = list.insert(&1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&&1), list.get(&entry));
assert_eq!(Some(&mut &1), list.get_mut(&entry));
}
#[test]
fn entry_list_multi_insert_reference() {
let mut list: EntryList<&usize> = EntryList::new();
let entry = list.insert(&1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&&1), list.get(&entry));
assert_eq!(Some(&mut &1), list.get_mut(&entry));
let entry2 = list.insert(&2);
assert_eq!(entry2, Entry::new(1, 0));
let entry3 = list.insert(&3);
assert_eq!(entry3, Entry::new(2, 0));
}
#[test]
fn entry_list_can_select_take() {
let mut list: EntryList<usize> = EntryList::new();
list.insert(1);
list.insert(2);
assert_eq!(2, list.allocated_slots());
assert_eq!(0, list.open_slots());
assert_eq!(2, list.active_slots());
let values = list.select_take(|_| true);
assert_eq!(vec![1, 2], values);
assert_eq!(2, list.allocated_slots());
assert_eq!(2, list.open_slots());
assert_eq!(0, list.active_slots());
}
#[test]
fn entry_list_can_park_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
assert_eq!(Some(&mut 1), list.get_mut(&entry));
assert_eq!(Some(1), list.park(&entry));
assert_eq!(1, list.allocated_slots());
assert_eq!(0, list.open_slots());
assert_eq!(1, list.parked_slots());
assert_eq!(1, list.active_slots());
assert!(list.unpark(&entry, 2));
assert_eq!(Some(&2), list.get(&entry));
assert_eq!(1, list.allocated_slots());
assert_eq!(0, list.open_slots());
assert_eq!(0, list.parked_slots());
assert_eq!(1, list.active_slots());
}
#[test]
fn entry_list_can_take_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
assert_eq!(Some(&mut 1), list.get_mut(&entry));
assert_eq!(Some(1), list.take(&entry));
assert_eq!(1, list.allocated_slots());
assert_eq!(1, list.open_slots());
assert_eq!(0, list.active_slots());
}
#[test]
fn entry_list_insert_value() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
assert_eq!(Some(&mut 1), list.get_mut(&entry));
}
#[test]
fn entry_list_can_vacate_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
list.vacate(&entry);
assert_eq!(None, list.get(&entry));
assert_eq!(1, list.allocated_slots());
assert_eq!(1, list.open_slots());
assert_eq!(0, list.active_slots());
}
#[test]
fn entry_list_can_check_entry_validity() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
assert!(list.has(&entry));
}
#[test]
fn entry_list_can_check_if_is_invalid_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
let (_, old_value) = list.replace(&entry, 2).expect("should have value");
assert_eq!(1, old_value);
assert!(list.not_valid(&entry));
}
#[test]
#[traced_test]
fn entry_list_can_replace_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
let (new_entry, old_value) = list.replace(&entry, 2).expect("should have value");
assert_eq!(new_entry, Entry::new(0, 1));
assert_eq!(1, old_value);
assert_eq!(None, list.get(&entry));
assert_eq!(Some(&2), list.get(&new_entry));
assert_eq!(new_entry.id, entry.id);
}
#[test]
fn entry_list_can_update_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
assert_eq!(Some(1), list.update(&entry, 2));
assert_eq!(Some(&2), list.get(&entry));
}
#[test]
fn entry_list_can_modify_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(1);
assert_eq!(entry, Entry::new(0, 0));
assert_eq!(Some(&1), list.get(&entry));
if let Some(value) = list.get_mut(&entry) {
*value = 2;
}
assert_eq!(Some(&2), list.get(&entry));
}
}
#[cfg(test)]
mod test_entry_list_edge_cases {
use super::*;
#[test]
fn test_for_each_iteration() {
let mut list: EntryList<usize> = EntryList::new();
list.insert(1);
list.insert(2);
list.insert(3);
let sum = std::sync::Arc::new(std::sync::Mutex::new(0));
let sum_clone = sum.clone();
list.for_each(|value| {
if let Some(&v) = value {
*sum_clone.lock().unwrap() += v;
}
});
assert_eq!(6, *sum.lock().unwrap());
}
#[test]
fn test_map_with_transformation() {
let mut list: EntryList<usize> = EntryList::new();
list.insert(1);
list.insert(2);
list.insert(3);
let doubled: Vec<usize> = list.map_with(|&v| Some(v * 2));
assert_eq!(vec![2, 4, 6], doubled);
}
#[test]
fn test_map_with_filtering() {
let mut list: EntryList<usize> = EntryList::new();
list.insert(1);
list.insert(2);
list.insert(3);
list.insert(4);
let evens: Vec<usize> = list.map_with(|&v| if v % 2 == 0 { Some(v) } else { None });
assert_eq!(vec![2, 4], evens);
}
#[test]
fn test_large_scale_insertions() {
let mut list: EntryList<usize> = EntryList::new();
let count = 1000;
let mut entries = Vec::new();
for i in 0..count {
entries.push(list.insert(i));
}
assert_eq!(count, list.active_slots());
assert_eq!(count, list.allocated_slots());
assert_eq!(0, list.open_slots());
for (i, entry) in entries.iter().enumerate() {
assert_eq!(Some(&i), list.get(entry));
}
}
#[test]
fn test_alternating_insert_remove() {
let mut list: EntryList<usize> = EntryList::new();
for i in 0..100 {
let entry = list.insert(i);
assert_eq!(1, list.active_slots());
list.take(&entry);
assert_eq!(0, list.active_slots());
assert_eq!(1, list.open_slots());
}
assert_eq!(1, list.allocated_slots());
assert_eq!(1, list.open_slots());
}
#[test]
fn test_generation_increment_on_reuse() {
let mut list: EntryList<usize> = EntryList::new();
let entry1 = list.insert(1);
list.take(&entry1);
let entry2 = list.insert(2);
assert!(entry1.id == entry2.id);
assert!(entry1.gen != entry2.gen);
assert!(list.not_valid(&entry1));
assert!(list.has(&entry2));
}
#[test]
fn test_multiple_select_take_operations() {
let mut list: EntryList<usize> = EntryList::new();
for i in 0..10 {
list.insert(i);
}
let evens = list.select_take(|&v| v % 2 == 0);
assert_eq!(5, evens.len());
assert_eq!(5, list.active_slots());
let high = list.select_take(|&v| v > 5);
assert_eq!(2, high.len());
assert_eq!(3, list.active_slots());
}
#[test]
fn test_vacate_vs_take() {
let mut list: EntryList<String> = EntryList::new();
let entry1 = list.insert("value1".to_string());
let entry2 = list.insert("value2".to_string());
list.vacate(&entry1);
assert_eq!(None, list.get(&entry1));
assert_eq!(1, list.open_slots());
let value = list.take(&entry2);
assert_eq!(Some("value2".to_string()), value);
assert_eq!(2, list.open_slots());
}
#[test]
fn test_park_and_unpark_cycle() {
let mut list: EntryList<usize> = EntryList::new();
let entry = list.insert(100);
assert_eq!(1, list.active_slots());
assert_eq!(0, list.parked_slots());
let parked_value = list.park(&entry);
assert_eq!(Some(100), parked_value);
assert_eq!(1, list.active_slots()); assert_eq!(1, list.parked_slots());
assert_eq!(None, list.get(&entry));
assert!(list.unpark(&entry, 200));
assert_eq!(Some(&200), list.get(&entry));
assert_eq!(0, list.parked_slots());
assert_eq!(1, list.active_slots());
}
#[test]
fn test_unpark_nonexistent_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry = Entry::new(999, 0);
assert!(!list.unpark(&entry, 100));
}
#[test]
fn test_replace_invalidates_old_entry() {
let mut list: EntryList<usize> = EntryList::new();
let entry1 = list.insert(10);
assert!(list.has(&entry1));
let (entry2, old_value) = list.replace(&entry1, 20).unwrap();
assert_eq!(10, old_value);
assert!(list.not_valid(&entry1));
assert!(!list.has(&entry1));
assert!(list.has(&entry2));
assert_eq!(Some(&20), list.get(&entry2));
}
#[test]
fn test_mixed_operations_stress() {
let mut list: EntryList<usize> = EntryList::new();
let mut active_entries = Vec::new();
for i in 0..50 {
active_entries.push(list.insert(i));
}
for i in (0..25).rev() {
list.take(&active_entries.remove(i));
}
assert_eq!(25, list.active_slots());
assert_eq!(25, list.open_slots());
for entry in active_entries.iter().take(10) {
list.update(entry, 999);
}
let mut new_entries = Vec::new();
for entry in active_entries.iter().skip(10).take(5) {
if let Some((new_entry, _)) = list.replace(entry, 888) {
new_entries.push(new_entry);
}
}
for entry in new_entries.iter() {
assert_eq!(Some(&888), list.get(entry));
}
}
}
#[cfg(test)]
mod test_thread_safe_entry {
use super::*;
use std::sync::Arc;
use std::thread;
#[test]
fn test_thread_safe_entry_new() {
let safe_list: ThreadSafeEntry<usize> = ThreadSafeEntry::new();
assert_eq!(0, safe_list.active_slots());
}
#[test]
fn test_thread_safe_entry_default() {
let safe_list: ThreadSafeEntry<usize> = ThreadSafeEntry::default();
assert_eq!(0, safe_list.active_slots());
}
#[test]
fn test_thread_safe_entry_from_list() {
let mut list: EntryList<usize> = EntryList::new();
list.insert(1);
list.insert(2);
let safe_list = ThreadSafeEntry::from(list);
assert_eq!(2, safe_list.active_slots());
}
#[test]
fn test_thread_safe_entry_concurrent_inserts() {
let safe_list = Arc::new(ThreadSafeEntry::<usize>::new());
let mut handles = vec![];
for i in 0..10 {
let list_clone = safe_list.clone();
handles.push(thread::spawn(move || list_clone.insert(i)));
}
let entries: Vec<Entry> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(10, safe_list.active_slots());
assert_eq!(10, entries.len());
}
#[test]
fn test_thread_safe_entry_concurrent_reads() {
let safe_list = Arc::new(ThreadSafeEntry::<usize>::new());
let entries: Vec<Entry> = (0..5).map(|i| safe_list.insert(i * 10)).collect();
let mut handles = vec![];
for entry in entries {
let list_clone = safe_list.clone();
handles.push(thread::spawn(move || {
let mut result = None;
list_clone.get(&entry, |value| {
result = value.copied();
});
result
}));
}
let results: Vec<Option<usize>> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(5, results.iter().filter(|r| r.is_some()).count());
}
#[test]
fn test_thread_safe_entry_get_mut() {
let safe_list = ThreadSafeEntry::<usize>::new();
let entry = safe_list.insert(10);
safe_list.get_mut(&entry, |value| {
if let Some(v) = value {
*v = 20;
}
});
let mut result = None;
safe_list.get(&entry, |value| {
result = value.copied();
});
assert_eq!(Some(20), result);
}
#[test]
fn test_thread_safe_entry_park_unpark() {
let safe_list = ThreadSafeEntry::<usize>::new();
let entry = safe_list.insert(100);
assert_eq!(0, safe_list.parked_slots());
let parked = safe_list.park(&entry);
assert_eq!(Some(100), parked);
assert_eq!(1, safe_list.parked_slots());
assert!(safe_list.unpark(&entry, 200));
assert_eq!(0, safe_list.parked_slots());
let mut result = None;
safe_list.get(&entry, |value| {
result = value.copied();
});
assert_eq!(Some(200), result);
}
#[test]
fn test_thread_safe_entry_concurrent_mixed_ops() {
let safe_list = Arc::new(ThreadSafeEntry::<usize>::new());
let entries: Vec<Entry> = (0..20).map(|i| safe_list.insert(i)).collect();
let mut read_handles = vec![];
let mut update_handles = vec![];
for entry in entries.iter().take(10) {
let list_clone = safe_list.clone();
let entry_copy = *entry;
read_handles.push(thread::spawn(move || {
let mut found = false;
list_clone.get(&entry_copy, |value| {
found = value.is_some();
});
found
}));
}
for entry in entries.iter().skip(10) {
let list_clone = safe_list.clone();
let entry_copy = *entry;
update_handles.push(thread::spawn(move || list_clone.update(&entry_copy, 999)));
}
for handle in read_handles {
handle.join().unwrap();
}
for handle in update_handles {
handle.join().unwrap();
}
assert_eq!(20, safe_list.active_slots());
}
#[test]
fn test_thread_safe_entry_select_take() {
let safe_list = ThreadSafeEntry::<usize>::new();
for i in 0..10 {
safe_list.insert(i);
}
let evens = safe_list.select_take(|&v| v % 2 == 0);
assert_eq!(5, evens.len());
assert_eq!(5, safe_list.active_slots());
}
#[test]
fn test_thread_safe_entry_map_with() {
let safe_list = ThreadSafeEntry::<usize>::new();
for i in 1..=5 {
safe_list.insert(i);
}
let doubled: Vec<usize> = safe_list.map_with(|&v| Some(v * 2));
assert_eq!(vec![2, 4, 6, 8, 10], doubled);
}
#[test]
fn test_thread_safe_entry_for_each() {
let safe_list = ThreadSafeEntry::<usize>::new();
for i in 1..=5 {
safe_list.insert(i);
}
let sum = std::sync::Arc::new(std::sync::Mutex::new(0));
let sum_clone = sum.clone();
safe_list.for_each(|value| {
if let Some(&v) = value {
*sum_clone.lock().unwrap() += v;
}
});
assert_eq!(15, *sum.lock().unwrap());
}
}