diff --git a/Cargo.toml b/Cargo.toml index c78fd2f93..a6300df4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ dashmap = { version = "6", features = ["raw-api"] } hashlink = "0.9" hashbrown = "0.14.3" indexmap = "2" -append-only-vec = "0.1.5" +boxcar = { git = "https://github.com/ibraheemdev/boxcar", rev = "4d51fcadc841f16caafa9decc933fffd135fd6fa" } tracing = "0.1" parking_lot = "0.12" rustc-hash = "2" diff --git a/src/input.rs b/src/input.rs index 434293302..2112c46bc 100644 --- a/src/input.rs +++ b/src/input.rs @@ -194,7 +194,7 @@ impl IngredientImpl { .table() .pages .iter() - .filter_map(|page| page.cast_type::>>()) + .filter_map(|(_, page)| page.cast_type::>>()) .flat_map(|page| page.slots()) } diff --git a/src/interned.rs b/src/interned.rs index c31adeac3..fb6720b48 100644 --- a/src/interned.rs +++ b/src/interned.rs @@ -263,7 +263,7 @@ where .table() .pages .iter() - .filter_map(|page| page.cast_type::>>()) + .filter_map(|(_, page)| page.cast_type::>>()) .flat_map(|page| page.slots()) } diff --git a/src/table.rs b/src/table.rs index 16b75130c..e3def5fa4 100644 --- a/src/table.rs +++ b/src/table.rs @@ -7,7 +7,6 @@ use std::{ sync::atomic::{AtomicUsize, Ordering}, }; -use append_only_vec::AppendOnlyVec; use memo::MemoTable; use parking_lot::Mutex; use sync::SyncTable; @@ -24,7 +23,7 @@ const PAGE_LEN: usize = 1 << PAGE_LEN_BITS; const MAX_PAGES: usize = 1 << (32 - PAGE_LEN_BITS); pub struct Table { - pub(crate) pages: AppendOnlyVec>, + pub(crate) pages: boxcar::Vec>, } pub(crate) trait TablePage: Any + Send + Sync { @@ -118,7 +117,7 @@ impl SlotIndex { impl Default for Table { fn default() -> Self { Self { - pages: AppendOnlyVec::new(), + pages: boxcar::Vec::new(), } } } @@ -179,7 +178,12 @@ impl Table { /// Get the memo table associated with `id` pub(crate) fn memos_mut(&mut self, id: Id) -> &mut MemoTable { let (page, slot) = split_id(id); - self.pages[page.0].memos_mut(slot) + let page_index = page.0; + let page = self + .pages + .get_mut(page_index) + .unwrap_or_else(|| panic!("index `{page_index}` is uninitialized")); + page.memos_mut(slot) } /// Get the sync table associated with `id` diff --git a/src/tracked_struct.rs b/src/tracked_struct.rs index cff5e2ee8..282acdbef 100644 --- a/src/tracked_struct.rs +++ b/src/tracked_struct.rs @@ -691,7 +691,7 @@ where .table() .pages .iter() - .filter_map(|page| page.cast_type::>>()) + .filter_map(|(_, page)| page.cast_type::>>()) .flat_map(|page| page.slots()) } } diff --git a/src/views.rs b/src/views.rs index d9f01de06..61ca54cc9 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1,5 +1,4 @@ use crate::{zalsa::transmute_data_ptr, Database}; -use append_only_vec::AppendOnlyVec; use std::{ any::{Any, TypeId}, sync::Arc, @@ -24,7 +23,7 @@ use std::{ #[derive(Clone)] pub struct Views { source_type_id: TypeId, - view_casters: Arc>, + view_casters: Arc>, } /// A DynViewCaster contains a manual trait object that can cast from the @@ -78,7 +77,7 @@ impl Views { let source_type_id = TypeId::of::(); Self { source_type_id, - view_casters: Arc::new(AppendOnlyVec::new()), + view_casters: Arc::new(boxcar::Vec::new()), } } @@ -91,7 +90,7 @@ impl Views { if self .view_casters .iter() - .any(|u| u.target_type_id == target_type_id) + .any(|(_, u)| u.target_type_id == target_type_id) { return; } @@ -120,7 +119,7 @@ impl Views { assert_eq!(self.source_type_id, db_type_id, "database type mismatch"); let view_type_id = TypeId::of::(); - for view in self.view_casters.iter() { + for (_idx, view) in self.view_casters.iter() { if view.target_type_id == view_type_id { // SAFETY: We verified that this is the view caster for the // `DbView` type by checking type IDs above. diff --git a/src/zalsa.rs b/src/zalsa.rs index 6c9f7c20c..b812da470 100644 --- a/src/zalsa.rs +++ b/src/zalsa.rs @@ -1,8 +1,8 @@ -use append_only_vec::AppendOnlyVec; use parking_lot::{Mutex, RwLock}; use rustc_hash::FxHashMap; use std::any::{Any, TypeId}; use std::marker::PhantomData; +use std::ops::Deref; use std::thread::ThreadId; use crate::cycle::CycleRecoveryStrategy; @@ -139,10 +139,10 @@ pub struct Zalsa { /// Vector of ingredients. /// /// Immutable unless the mutex on `ingredients_map` is held. - ingredients_vec: AppendOnlyVec>, + ingredients_vec: boxcar::Vec>, /// Indices of ingredients that require reset when a new revision starts. - ingredients_requiring_reset: AppendOnlyVec, + ingredients_requiring_reset: boxcar::Vec, /// The runtime for this particular salsa database handle. /// Each handle gets its own runtime, but the runtimes have shared state between them. @@ -155,8 +155,8 @@ impl Zalsa { views_of: Views::new::(), nonce: NONCE.nonce(), jar_map: Default::default(), - ingredients_vec: AppendOnlyVec::new(), - ingredients_requiring_reset: AppendOnlyVec::new(), + ingredients_vec: boxcar::Vec::new(), + ingredients_requiring_reset: boxcar::Vec::new(), runtime: Runtime::default(), memo_ingredient_indices: Default::default(), } @@ -201,7 +201,7 @@ impl Zalsa { // ingredient indices cannot overlap. let index = *jar_map.entry(jar_type_id).or_insert_with(|| { should_create = true; - IngredientIndex::from(self.ingredients_vec.len()) + IngredientIndex::from(self.ingredients_vec.count()) }); if should_create { let aux = JarAuxImpl(self, &jar_map); @@ -210,10 +210,17 @@ impl Zalsa { let expected_index = ingredient.ingredient_index(); if ingredient.requires_reset_for_new_revision() { - self.ingredients_requiring_reset.push(expected_index); + // Safety: We hold the lock. + unsafe { + self.ingredients_requiring_reset + .push_single_writer(expected_index); + } } - let actual_index = self.ingredients_vec.push(ingredient); + // Safety: We hold the lock. + let actual_index = + unsafe { self.ingredients_vec.push_single_writer(ingredient) }; + assert_eq!( expected_index.as_usize(), actual_index, @@ -230,7 +237,12 @@ impl Zalsa { } pub(crate) fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient { - &*self.ingredients_vec[index.as_usize()] + let index = index.as_usize(); + let ingredient = self + .ingredients_vec + .get(index) + .unwrap_or_else(|| panic!("index `{index}` is uninitialized")); + ingredient.deref() } /// **NOT SEMVER STABLE** @@ -238,10 +250,12 @@ impl Zalsa { &mut self, index: IngredientIndex, ) -> (&mut dyn Ingredient, &mut Runtime) { - ( - &mut *self.ingredients_vec[index.as_usize()], - &mut self.runtime, - ) + let index = index.as_usize(); + let ingredient = self + .ingredients_vec + .get_mut(index) + .unwrap_or_else(|| panic!("index `{index}` is uninitialized")); + (ingredient.as_mut(), &mut self.runtime) } /// **NOT SEMVER STABLE** @@ -271,8 +285,14 @@ impl Zalsa { pub(crate) fn new_revision(&mut self) -> Revision { let new_revision = self.runtime.new_revision(); - for index in self.ingredients_requiring_reset.iter() { - self.ingredients_vec[index.as_usize()].reset_for_new_revision(self.runtime.table_mut()); + for (_, index) in self.ingredients_requiring_reset.iter() { + let index = index.as_usize(); + let ingredient = self + .ingredients_vec + .get_mut(index) + .unwrap_or_else(|| panic!("index `{index}` is uninitialized")); + + ingredient.reset_for_new_revision(self.runtime.table_mut()); } new_revision