diff --git a/libs/neon-shmem/src/hash.rs b/libs/neon-shmem/src/hash.rs index efe7c96cb3..404a7ade50 100644 --- a/libs/neon-shmem/src/hash.rs +++ b/libs/neon-shmem/src/hash.rs @@ -22,16 +22,16 @@ pub mod entry; mod tests; use core::{CoreHashMap, INVALID_POS}; -use entry::{Entry, OccupiedEntry, PrevPos}; +use entry::{Entry, OccupiedEntry}; -#[derive(Debug)] -pub struct OutOfMemoryError(); pub struct HashMapInit<'a, K, V, S = rustc_hash::FxBuildHasher> { // Hash table can be allocated in a fixed memory area, or in a resizeable ShmemHandle. shmem_handle: Option, shared_ptr: *mut HashMapShared<'a, K, V>, + shared_size: usize, hasher: S, + num_buckets: u32, } pub struct HashMapAccess<'a, K, V, S = rustc_hash::FxBuildHasher> { @@ -43,8 +43,46 @@ pub struct HashMapAccess<'a, K, V, S = rustc_hash::FxBuildHasher> { unsafe impl<'a, K: Sync, V: Sync, S> Sync for HashMapAccess<'a, K, V, S> {} unsafe impl<'a, K: Send, V: Send, S> Send for HashMapAccess<'a, K, V, S> {} -impl<'a, K, V, S> HashMapInit<'a, K, V, S> { +impl<'a, K: Clone + Hash + Eq, V, S> HashMapInit<'a, K, V, S> { + pub fn with_hasher(self, hasher: S) -> HashMapInit<'a, K, V, S> { + Self { hasher, ..self } + } + + pub fn estimate_size(num_buckets: u32) -> usize { + // add some margin to cover alignment etc. + CoreHashMap::::estimate_size(num_buckets) + size_of::>() + 1000 + } + pub fn attach_writer(self) -> HashMapAccess<'a, K, V, S> { + // carve out the HashMapShared struct from the area. + let mut ptr: *mut u8 = self.shared_ptr.cast(); + let end_ptr: *mut u8 = unsafe { ptr.add(self.shared_size) }; + ptr = unsafe { ptr.add(ptr.align_offset(align_of::>())) }; + let shared_ptr: *mut HashMapShared = ptr.cast(); + ptr = unsafe { ptr.add(size_of::>()) }; + + // carve out the buckets + ptr = unsafe { ptr.byte_add(ptr.align_offset(align_of::>())) }; + let buckets_ptr = ptr; + ptr = unsafe { ptr.add(size_of::>() * self.num_buckets as usize) }; + + // use remaining space for the dictionary + ptr = unsafe { ptr.byte_add(ptr.align_offset(align_of::())) }; + assert!(ptr.addr() < end_ptr.addr()); + let dictionary_ptr = ptr; + let dictionary_size = unsafe { end_ptr.byte_offset_from(ptr) / size_of::() as isize }; + assert!(dictionary_size > 0); + + let buckets = + unsafe { std::slice::from_raw_parts_mut(buckets_ptr.cast(), self.num_buckets as usize) }; + let dictionary = unsafe { + std::slice::from_raw_parts_mut(dictionary_ptr.cast(), dictionary_size as usize) + }; + let hashmap = CoreHashMap::new(buckets, dictionary); + unsafe { + std::ptr::write(shared_ptr, HashMapShared { inner: hashmap }); + } + HashMapAccess { shmem_handle: self.shmem_handle, shared_ptr: self.shared_ptr, @@ -77,88 +115,56 @@ impl<'a, K, V> HashMapInit<'a, K, V, rustc_hash::FxBuildHasher> where K: Clone + Hash + Eq { - pub fn init_in_fixed_area( - num_buckets: u32, + pub fn with_fixed( + num_buckets: u32, area: &'a mut [MaybeUninit], ) -> HashMapInit<'a, K, V> { - Self::init_in_fixed_area_with_hasher(num_buckets, area, rustc_hash::FxBuildHasher::default()) + Self { + num_buckets, + shmem_handle: None, + shared_ptr: area.as_mut_ptr().cast(), + shared_size: area.len(), + hasher: rustc_hash::FxBuildHasher::default(), + } } /// Initialize a new hash map in the given shared memory area - pub fn init_in_shmem(num_buckets: u32, shmem: ShmemHandle) -> HashMapInit<'a, K, V> { - Self::init_in_shmem_with_hasher(num_buckets, shmem, rustc_hash::FxBuildHasher::default()) - } -} - -impl<'a, K, V, S: BuildHasher> HashMapInit<'a, K, V, S> -where - K: Clone + Hash + Eq -{ - pub fn estimate_size(num_buckets: u32) -> usize { - // add some margin to cover alignment etc. - CoreHashMap::::estimate_size(num_buckets) + size_of::>() + 1000 - } - - pub fn init_in_shmem_with_hasher(num_buckets: u32, mut shmem: ShmemHandle, hasher: S) -> HashMapInit<'a, K, V, S> { - let size = Self::estimate_size(num_buckets); - shmem + pub fn with_shmem(num_buckets: u32, shmem: ShmemHandle) -> HashMapInit<'a, K, V> { + let size = Self::estimate_size(num_buckets); + shmem .set_size(size) .expect("could not resize shared memory area"); - - let ptr = unsafe { shmem.data_ptr.as_mut() }; - Self::init_common(num_buckets, Some(shmem), ptr, size, hasher) + Self { + num_buckets, + shared_ptr: shmem.data_ptr.as_ptr().cast(), + shmem_handle: Some(shmem), + shared_size: size, + hasher: rustc_hash::FxBuildHasher::default() + } } - pub fn init_in_fixed_area_with_hasher( - num_buckets: u32, - area: &'a mut [MaybeUninit], - hasher: S, - ) -> HashMapInit<'a, K, V, S> { - Self::init_common(num_buckets, None, area.as_mut_ptr().cast(), area.len(), hasher) - } - - fn init_common( - num_buckets: u32, - shmem_handle: Option, - area_ptr: *mut u8, - area_len: usize, - hasher: S, - ) -> HashMapInit<'a, K, V, S> { - // carve out the HashMapShared struct from the area. - let mut ptr: *mut u8 = area_ptr; - let end_ptr: *mut u8 = unsafe { area_ptr.add(area_len) }; - ptr = unsafe { ptr.add(ptr.align_offset(align_of::>())) }; - let shared_ptr: *mut HashMapShared = ptr.cast(); - ptr = unsafe { ptr.add(size_of::>()) }; + pub fn new_resizeable_named(num_buckets: u32, max_buckets: u32, name: &str) -> HashMapInit<'a, K, V> { + let size = Self::estimate_size(num_buckets); + let max_size = Self::estimate_size(max_buckets); + let shmem = ShmemHandle::new(name, size, max_size) + .expect("failed to make shared memory area"); + + Self { + num_buckets, + shared_ptr: shmem.data_ptr.as_ptr().cast(), + shmem_handle: Some(shmem), + shared_size: size, + hasher: rustc_hash::FxBuildHasher::default() + } + } - // carve out the buckets - ptr = unsafe { ptr.byte_add(ptr.align_offset(align_of::>())) }; - let buckets_ptr = ptr; - ptr = unsafe { ptr.add(size_of::>() * num_buckets as usize) }; - - // use remaining space for the dictionary - ptr = unsafe { ptr.byte_add(ptr.align_offset(align_of::())) }; - assert!(ptr.addr() < end_ptr.addr()); - let dictionary_ptr = ptr; - let dictionary_size = unsafe { end_ptr.byte_offset_from(ptr) / size_of::() as isize }; - assert!(dictionary_size > 0); - - let buckets = - unsafe { std::slice::from_raw_parts_mut(buckets_ptr.cast(), num_buckets as usize) }; - let dictionary = unsafe { - std::slice::from_raw_parts_mut(dictionary_ptr.cast(), dictionary_size as usize) - }; - let hashmap = CoreHashMap::new(buckets, dictionary); - unsafe { - std::ptr::write(shared_ptr, HashMapShared { inner: hashmap }); - } - - HashMapInit { - shmem_handle, - shared_ptr, - hasher, - } - } + pub fn new_resizeable(num_buckets: u32, max_buckets: u32) -> HashMapInit<'a, K, V> { + use std::sync::atomic::{AtomicUsize, Ordering}; + const COUNTER: AtomicUsize = AtomicUsize::new(0); + let val = COUNTER.fetch_add(1, Ordering::Relaxed); + let name = format!("neon_shmem_hmap{}", val); + Self::new_resizeable_named(num_buckets, max_buckets, &name) + } } impl<'a, K, V, S: BuildHasher> HashMapAccess<'a, K, V, S> @@ -248,6 +254,8 @@ where num_buckets: u32, rehash_buckets: u32, ) { + inner.free_head = INVALID_POS; + // Recalculate the dictionary let buckets; let dictionary; @@ -268,7 +276,9 @@ where for i in 0..rehash_buckets as usize { if buckets[i].inner.is_none() { - continue; + buckets[i].next = inner.free_head; + inner.free_head = i as u32; + continue; } let hash = self.hasher.hash_one(&buckets[i].inner.as_ref().unwrap().0); @@ -286,19 +296,13 @@ where pub fn shuffle(&mut self) { let map = unsafe { self.shared_ptr.as_mut() }.unwrap(); let inner = &mut map.inner; - - let shmem_handle = self - .shmem_handle - .as_ref() - .expect("TODO(quantumish): make shuffle work w/ fixed-size table"); let num_buckets = inner.get_num_buckets() as u32; let size_bytes = HashMapInit::::estimate_size(num_buckets); - let end_ptr: *mut u8 = unsafe { shmem_handle.data_ptr.as_ptr().add(size_bytes) }; + let end_ptr: *mut u8 = unsafe { (self.shared_ptr as *mut u8).add(size_bytes) }; let buckets_ptr = inner.buckets.as_mut_ptr(); self.rehash_dict(inner, buckets_ptr, end_ptr, num_buckets, num_buckets); } - /// Grow /// /// 1. grow the underlying shared memory area @@ -336,11 +340,6 @@ where } else { inner.free_head }, - prev: if i > 0 { - PrevPos::Chained(i as u32 - 1) - } else { - PrevPos::First(INVALID_POS) - }, inner: None, }); } @@ -352,7 +351,7 @@ where Ok(()) } - /// Begin a shrink, limiting all new allocations to be in buckets with index less than `num_buckets`. + /// Begin a shrink, limiting all new allocations to be in buckets with index below `num_buckets`. pub fn begin_shrink(&mut self, num_buckets: u32) { let map = unsafe { self.shared_ptr.as_mut() }.unwrap(); if num_buckets > map.inner.get_num_buckets() as u32 { @@ -365,6 +364,26 @@ where map.inner.alloc_limit = num_buckets; } + /// Returns whether a shrink operation is currently in progress. + pub fn is_shrinking(&self) -> bool { + let map = unsafe { self.shared_ptr.as_mut() }.unwrap(); + map.inner.is_shrinking() + } + + /// Returns how many entries need to be evicted before shrink can complete. + pub fn shrink_remaining(&self) -> usize { + let map = unsafe { self.shared_ptr.as_mut() }.unwrap(); + let inner = &mut map.inner; + if !inner.is_shrinking() { + panic!("shrink_remaining called when no ongoing shrink") + } else { + inner.buckets_in_use + .checked_sub(inner.alloc_limit) + .unwrap_or(0) + as usize + } + } + /// Complete a shrink after caller has evicted entries, removing the unused buckets and rehashing. pub fn finish_shrink(&mut self) -> Result<(), crate::shmem::Error> { let map = unsafe { self.shared_ptr.as_mut() }.unwrap(); @@ -377,30 +396,24 @@ where if inner.get_num_buckets() == num_buckets as usize { return Ok(()); - } else if inner.get_num_buckets() > num_buckets as usize { + } else if inner.buckets_in_use > num_buckets { panic!("called finish_shrink before enough entries were removed"); } + + let mut open_spots = 0; + let mut curr = inner.free_head; + while curr != INVALID_POS { + if curr < num_buckets { + open_spots += 1; + } + curr = inner.buckets[curr as usize].next; + } for i in (num_buckets as usize)..inner.buckets.len() { - if let Some((k, v)) = inner.buckets[i].inner.take() { - inner.alloc_bucket(k, v, inner.buckets[i].prev.unwrap_first()).unwrap(); - } else { - match inner.buckets[i].prev { - PrevPos::First(_) => { - let next_pos = inner.buckets[i].next; - inner.free_head = next_pos; - if next_pos != INVALID_POS { - inner.buckets[next_pos as usize].prev = PrevPos::First(INVALID_POS); - } - }, - PrevPos::Chained(j) => { - let next_pos = inner.buckets[i].next; - inner.buckets[j as usize].next = next_pos; - if next_pos != INVALID_POS { - inner.buckets[next_pos as usize].prev = PrevPos::Chained(j); - } - } - } + if let Some((k, v)) = inner.buckets[i].inner.take() { + // alloc bucket increases buckets in use, so need to decrease since we're just moving + inner.buckets_in_use -= 1; + inner.alloc_bucket(k, v).unwrap(); } } diff --git a/libs/neon-shmem/src/hash/core.rs b/libs/neon-shmem/src/hash/core.rs index 049600e538..b27180a80a 100644 --- a/libs/neon-shmem/src/hash/core.rs +++ b/libs/neon-shmem/src/hash/core.rs @@ -12,14 +12,16 @@ pub(crate) const INVALID_POS: u32 = u32::MAX; // Bucket pub(crate) struct Bucket { - pub(crate) next: u32, - pub(crate) prev: PrevPos, + pub(crate) next: u32, pub(crate) inner: Option<(K, V)>, } pub(crate) struct CoreHashMap<'a, K, V> { + /// Dictionary used to map hashes to bucket indices. pub(crate) dictionary: &'a mut [u32], + /// Buckets containing key-value pairs. pub(crate) buckets: &'a mut [Bucket], + /// Head of the freelist. pub(crate) free_head: u32, pub(crate) _user_list_head: u32, @@ -63,12 +65,7 @@ where i as u32 + 1 } else { INVALID_POS - }, - prev: if i > 0 { - PrevPos::Chained(i as u32 - 1) - } else { - PrevPos::First(INVALID_POS) - }, + }, inner: None, }); } @@ -160,8 +157,8 @@ where self.alloc_limit != INVALID_POS } - - // TODO(quantumish): How does this interact with an ongoing shrink? + /// Clears all entries from the hashmap. + /// Does not reset any allocation limits, but does clear any entries beyond them. pub fn clear(&mut self) { for i in 0..self.buckets.len() { self.buckets[i] = Bucket { @@ -169,12 +166,7 @@ where i as u32 + 1 } else { INVALID_POS - }, - prev: if i > 0 { - PrevPos::Chained(i as u32 - 1) - } else { - PrevPos::First(INVALID_POS) - }, + }, inner: None, } } @@ -184,7 +176,6 @@ where } self.buckets_in_use = 0; - self.alloc_limit = INVALID_POS; } pub fn entry_at_bucket(&mut self, pos: usize) -> Option> { @@ -192,13 +183,12 @@ where return None; } - let prev = self.buckets[pos].prev; let entry = self.buckets[pos].inner.as_ref(); match entry { Some((key, _)) => Some(OccupiedEntry { _key: key.clone(), bucket_pos: pos as u32, - prev_pos: prev, + prev_pos: PrevPos::Unknown, map: self, }), _ => None, @@ -206,7 +196,7 @@ where } /// Find the position of an unused bucket via the freelist and initialize it. - pub(crate) fn alloc_bucket(&mut self, key: K, value: V, dict_pos: u32) -> Result { + pub(crate) fn alloc_bucket(&mut self, key: K, value: V) -> Result { let mut pos = self.free_head; // Find the first bucket we're *allowed* to use. @@ -225,17 +215,12 @@ where PrevPos::First(_) => { let next_pos = self.buckets[pos as usize].next; self.free_head = next_pos; - if next_pos != INVALID_POS { - self.buckets[next_pos as usize].prev = PrevPos::First(dict_pos); - } } PrevPos::Chained(p) => if p != INVALID_POS { let next_pos = self.buckets[pos as usize].next; self.buckets[p as usize].next = next_pos; - if next_pos != INVALID_POS { - self.buckets[next_pos as usize].prev = PrevPos::Chained(p); - } }, + PrevPos::Unknown => unreachable!() } // Initialize the bucket. diff --git a/libs/neon-shmem/src/hash/entry.rs b/libs/neon-shmem/src/hash/entry.rs index 7d3091c754..63af840c5d 100644 --- a/libs/neon-shmem/src/hash/entry.rs +++ b/libs/neon-shmem/src/hash/entry.rs @@ -10,13 +10,19 @@ pub enum Entry<'a, 'b, K, V> { Vacant(VacantEntry<'a, 'b, K, V>), } +/// Helper enum representing the previous position within a hashmap chain. #[derive(Clone, Copy)] pub(crate) enum PrevPos { + /// Starting index within the dictionary. First(u32), + /// Regular index within the buckets. Chained(u32), + /// Unknown - e.g. the associated entry was retrieved by index instead of chain. + Unknown, } impl PrevPos { + /// Unwrap an index from a `PrevPos::First`, panicking otherwise. pub fn unwrap_first(&self) -> u32 { match self { Self::First(i) => *i, @@ -26,10 +32,13 @@ impl PrevPos { } pub struct OccupiedEntry<'a, 'b, K, V> { - pub(crate) map: &'b mut CoreHashMap<'a, K, V>, - pub(crate) _key: K, // The key of the occupied entry + pub(crate) map: &'b mut CoreHashMap<'a, K, V>, + /// The key of the occupied entry + pub(crate) _key: K, + /// The index of the previous entry in the chain. pub(crate) prev_pos: PrevPos, - pub(crate) bucket_pos: u32, // The position of the bucket in the CoreHashMap's buckets array + /// The position of the bucket in the CoreHashMap's buckets array. + pub(crate) bucket_pos: u32, } impl<'a, 'b, K, V> OccupiedEntry<'a, 'b, K, V> { @@ -65,17 +74,14 @@ impl<'a, 'b, K, V> OccupiedEntry<'a, 'b, K, V> { PrevPos::First(dict_pos) => self.map.dictionary[dict_pos as usize] = bucket.next, PrevPos::Chained(bucket_pos) => { self.map.buckets[bucket_pos as usize].next = bucket.next - } + }, + PrevPos::Unknown => panic!("can't safely remove entry with unknown previous entry"), } // and add it to the freelist - if self.map.free_head != INVALID_POS { - self.map.buckets[self.map.free_head as usize].prev = PrevPos::Chained(self.bucket_pos); - } let bucket = &mut self.map.buckets[self.bucket_pos as usize]; let old_value = bucket.inner.take(); bucket.next = self.map.free_head; - bucket.prev = PrevPos::First(INVALID_POS); self.map.free_head = self.bucket_pos; self.map.buckets_in_use -= 1; @@ -91,14 +97,11 @@ pub struct VacantEntry<'a, 'b, K, V> { impl<'a, 'b, K: Clone + Hash + Eq, V> VacantEntry<'a, 'b, K, V> { pub fn insert(self, value: V) -> Result<&'b mut V, FullError> { - let pos = self.map.alloc_bucket(self.key, value, self.dict_pos)?; + let pos = self.map.alloc_bucket(self.key, value)?; if pos == INVALID_POS { return Err(FullError()); } let bucket = &mut self.map.buckets[pos as usize]; - if let PrevPos::First(INVALID_POS) = bucket.prev { - bucket.prev = PrevPos::First(self.dict_pos); - } bucket.next = self.map.dictionary[self.dict_pos as usize]; self.map.dictionary[self.dict_pos as usize] = pos; diff --git a/libs/neon-shmem/src/hash/tests.rs b/libs/neon-shmem/src/hash/tests.rs index ee6acfc144..a522423db1 100644 --- a/libs/neon-shmem/src/hash/tests.rs +++ b/libs/neon-shmem/src/hash/tests.rs @@ -38,11 +38,9 @@ impl<'a> From<&'a [u8]> for TestKey { } fn test_inserts + Copy>(keys: &[K]) { - const MAX_MEM_SIZE: usize = 10000000; - let shmem = ShmemHandle::new("test_inserts", 0, MAX_MEM_SIZE).unwrap(); - - let init_struct = HashMapInit::::init_in_shmem(100000, shmem); - let mut w = init_struct.attach_writer(); + let mut w = HashMapInit::::new_resizeable_named( + 100000, 120000, "test_inserts" + ).attach_writer(); for (idx, k) in keys.iter().enumerate() { let hash = w.get_hash_value(&(*k).into()); @@ -193,24 +191,23 @@ fn do_shrink( to: u32 ) { writer.begin_shrink(to); - for i in to..from { - if let Some(entry) = writer.entry_at_bucket(i as usize) { - shadow.remove(&entry._key); - entry.remove(); + while writer.get_num_buckets_in_use() > to as usize { + let (k, _) = shadow.pop_first().unwrap(); + let hash = writer.get_hash_value(&k); + let entry = writer.entry_with_hash(k, hash); + if let Entry::Occupied(mut e) = entry { + e.remove(); } } writer.finish_shrink().unwrap(); - } #[test] fn random_ops() { - let shmem = ShmemHandle::new("test_inserts", 0, 10000000).unwrap(); - - let init_struct = HashMapInit::::init_in_shmem(100000, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 100000, 120000, "test_random" + ).attach_writer(); let mut shadow: std::collections::BTreeMap = BTreeMap::new(); - let distribution = Zipf::new(u128::MAX as f64, 1.1).unwrap(); let mut rng = rand::rng(); @@ -227,11 +224,25 @@ fn random_ops() { } } + +#[test] +fn test_shuffle() { + let mut writer = HashMapInit::::new_resizeable_named( + 1000, 1200, "test_shuf" + ).attach_writer(); + let mut shadow: std::collections::BTreeMap = BTreeMap::new(); + let mut rng = rand::rng(); + + do_random_ops(10000, 1000, 0.75, &mut writer, &mut shadow, &mut rng); + writer.shuffle(); + do_random_ops(10000, 1000, 0.75, &mut writer, &mut shadow, &mut rng); +} + #[test] fn test_grow() { - let shmem = ShmemHandle::new("test_grow", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1000, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1000, 2000, "test_grow" + ).attach_writer(); let mut shadow: std::collections::BTreeMap = BTreeMap::new(); let mut rng = rand::rng(); @@ -242,9 +253,9 @@ fn test_grow() { #[test] fn test_shrink() { - let shmem = ShmemHandle::new("test_shrink", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1500, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1500, 2000, "test_shrink" + ).attach_writer(); let mut shadow: std::collections::BTreeMap = BTreeMap::new(); let mut rng = rand::rng(); @@ -257,9 +268,9 @@ fn test_shrink() { #[test] fn test_shrink_grow_seq() { - let shmem = ShmemHandle::new("test_shrink_grow_seq", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1500, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1000, 20000, "test_grow_seq" + ).attach_writer(); let mut shadow: std::collections::BTreeMap = BTreeMap::new(); let mut rng = rand::rng(); @@ -281,9 +292,9 @@ fn test_shrink_grow_seq() { #[test] fn test_bucket_ops() { - let shmem = ShmemHandle::new("test_bucket_ops", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1000, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1000, 1200, "test_bucket_ops" + ).attach_writer(); let hash = writer.get_hash_value(&1.into()); match writer.entry_with_hash(1.into(), hash) { Entry::Occupied(mut e) => { e.insert(2); }, @@ -307,9 +318,9 @@ fn test_bucket_ops() { #[test] fn test_shrink_zero() { - let shmem = ShmemHandle::new("test_shrink_zero", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1500, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1500, 2000, "test_shrink_zero" + ).attach_writer(); writer.begin_shrink(0); for i in 0..1500 { writer.entry_at_bucket(i).map(|x| x.remove()); @@ -336,27 +347,27 @@ fn test_shrink_zero() { #[test] #[should_panic] fn test_grow_oom() { - let shmem = ShmemHandle::new("test_grow_oom", 0, 500).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(5, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1500, 2000, "test_grow_oom" + ).attach_writer(); writer.grow(20000).unwrap(); } #[test] #[should_panic] fn test_shrink_bigger() { - let shmem = ShmemHandle::new("test_shrink_bigger", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1500, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1500, 2500, "test_shrink_bigger" + ).attach_writer(); writer.begin_shrink(2000); } #[test] #[should_panic] fn test_shrink_early_finish() { - let shmem = ShmemHandle::new("test_shrink_early_finish", 0, 10000000).unwrap(); - let init_struct = HashMapInit::::init_in_shmem(1500, shmem); - let mut writer = init_struct.attach_writer(); + let mut writer = HashMapInit::::new_resizeable_named( + 1500, 2500, "test_shrink_early_finish" + ).attach_writer(); writer.finish_shrink().unwrap(); } @@ -364,7 +375,7 @@ fn test_shrink_early_finish() { #[should_panic] fn test_shrink_fixed_size() { let mut area = [MaybeUninit::uninit(); 10000]; - let init_struct = HashMapInit::::init_in_fixed_area(3, &mut area); + let init_struct = HashMapInit::::with_fixed(3, &mut area); let mut writer = init_struct.attach_writer(); writer.begin_shrink(1); }