diff --git a/components/salsa-macro-rules/src/setup_input_struct.rs b/components/salsa-macro-rules/src/setup_input_struct.rs index d25306b..1c0829c 100644 --- a/components/salsa-macro-rules/src/setup_input_struct.rs +++ b/components/salsa-macro-rules/src/setup_input_struct.rs @@ -203,7 +203,7 @@ macro_rules! setup_input_struct { /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl) pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { $zalsa::with_attached_database(|db| { - let fields = $Configuration::ingredient(db).leak_fields(this); + let fields = $Configuration::ingredient(db).leak_fields(db, this); let mut f = f.debug_struct(stringify!($Struct)); let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this)); $( @@ -235,7 +235,7 @@ macro_rules! setup_input_struct { let current_revision = $zalsa::current_revision(db); let ingredient = $Configuration::ingredient(db.as_dyn_database()); let (fields, stamps) = builder::builder_into_inner(self, current_revision); - ingredient.new_input(fields, stamps) + ingredient.new_input(db.as_dyn_database(), fields, stamps) } } diff --git a/examples/calc/parser.rs b/examples/calc/parser.rs index 15f2ad9..d92e5d3 100644 --- a/examples/calc/parser.rs +++ b/examples/calc/parser.rs @@ -378,25 +378,25 @@ fn parse_print() { let expected = expect_test::expect![[r#" ( Program { - [salsa id]: Id(400), + [salsa id]: Id(800), statements: [ Statement { span: Span { - [salsa id]: Id(4), + [salsa id]: Id(404), start: 0, end: 11, }, data: Print( Expression { span: Span { - [salsa id]: Id(3), + [salsa id]: Id(403), start: 6, end: 11, }, data: Op( Expression { span: Span { - [salsa id]: Id(0), + [salsa id]: Id(400), start: 6, end: 7, }, @@ -407,7 +407,7 @@ fn parse_print() { Add, Expression { span: Span { - [salsa id]: Id(2), + [salsa id]: Id(402), start: 10, end: 11, }, @@ -441,22 +441,22 @@ fn parse_example() { let expected = expect_test::expect![[r#" ( Program { - [salsa id]: Id(1000), + [salsa id]: Id(1400), statements: [ Statement { span: Span { - [salsa id]: Id(9), + [salsa id]: Id(409), start: 13, end: 57, }, data: Function( Function { - [salsa id]: Id(c00), + [salsa id]: Id(1000), name: FunctionId { text: "area_rectangle", }, name_span: Span { - [salsa id]: Id(0), + [salsa id]: Id(400), start: 16, end: 30, }, @@ -470,14 +470,14 @@ fn parse_example() { ], body: Expression { span: Span { - [salsa id]: Id(8), + [salsa id]: Id(408), start: 39, end: 57, }, data: Op( Expression { span: Span { - [salsa id]: Id(5), + [salsa id]: Id(405), start: 39, end: 41, }, @@ -490,7 +490,7 @@ fn parse_example() { Multiply, Expression { span: Span { - [salsa id]: Id(7), + [salsa id]: Id(407), start: 43, end: 57, }, @@ -507,18 +507,18 @@ fn parse_example() { }, Statement { span: Span { - [salsa id]: Id(15), + [salsa id]: Id(415), start: 57, end: 102, }, data: Function( Function { - [salsa id]: Id(c01), + [salsa id]: Id(1001), name: FunctionId { text: "area_circle", }, name_span: Span { - [salsa id]: Id(a), + [salsa id]: Id(40a), start: 60, end: 71, }, @@ -529,21 +529,21 @@ fn parse_example() { ], body: Expression { span: Span { - [salsa id]: Id(14), + [salsa id]: Id(414), start: 77, end: 102, }, data: Op( Expression { span: Span { - [salsa id]: Id(11), + [salsa id]: Id(411), start: 77, end: 86, }, data: Op( Expression { span: Span { - [salsa id]: Id(e), + [salsa id]: Id(40e), start: 77, end: 81, }, @@ -554,7 +554,7 @@ fn parse_example() { Multiply, Expression { span: Span { - [salsa id]: Id(10), + [salsa id]: Id(410), start: 84, end: 86, }, @@ -569,7 +569,7 @@ fn parse_example() { Multiply, Expression { span: Span { - [salsa id]: Id(13), + [salsa id]: Id(413), start: 88, end: 102, }, @@ -586,14 +586,14 @@ fn parse_example() { }, Statement { span: Span { - [salsa id]: Id(1c), + [salsa id]: Id(41c), start: 102, end: 141, }, data: Print( Expression { span: Span { - [salsa id]: Id(1b), + [salsa id]: Id(41b), start: 108, end: 128, }, @@ -604,7 +604,7 @@ fn parse_example() { [ Expression { span: Span { - [salsa id]: Id(17), + [salsa id]: Id(417), start: 123, end: 124, }, @@ -614,7 +614,7 @@ fn parse_example() { }, Expression { span: Span { - [salsa id]: Id(19), + [salsa id]: Id(419), start: 126, end: 127, }, @@ -629,14 +629,14 @@ fn parse_example() { }, Statement { span: Span { - [salsa id]: Id(21), + [salsa id]: Id(421), start: 141, end: 174, }, data: Print( Expression { span: Span { - [salsa id]: Id(20), + [salsa id]: Id(420), start: 147, end: 161, }, @@ -647,7 +647,7 @@ fn parse_example() { [ Expression { span: Span { - [salsa id]: Id(1e), + [salsa id]: Id(41e), start: 159, end: 160, }, @@ -662,21 +662,21 @@ fn parse_example() { }, Statement { span: Span { - [salsa id]: Id(26), + [salsa id]: Id(426), start: 174, end: 195, }, data: Print( Expression { span: Span { - [salsa id]: Id(25), + [salsa id]: Id(425), start: 180, end: 186, }, data: Op( Expression { span: Span { - [salsa id]: Id(22), + [salsa id]: Id(422), start: 180, end: 182, }, @@ -687,7 +687,7 @@ fn parse_example() { Multiply, Expression { span: Span { - [salsa id]: Id(24), + [salsa id]: Id(424), start: 185, end: 186, }, @@ -714,7 +714,7 @@ fn parse_error() { let expected = expect_test::expect![[r#" ( Program { - [salsa id]: Id(400), + [salsa id]: Id(800), statements: [], }, [ @@ -736,32 +736,32 @@ fn parse_precedence() { let expected = expect_test::expect![[r#" ( Program { - [salsa id]: Id(400), + [salsa id]: Id(800), statements: [ Statement { span: Span { - [salsa id]: Id(a), + [salsa id]: Id(40a), start: 0, end: 19, }, data: Print( Expression { span: Span { - [salsa id]: Id(9), + [salsa id]: Id(409), start: 6, end: 19, }, data: Op( Expression { span: Span { - [salsa id]: Id(6), + [salsa id]: Id(406), start: 6, end: 16, }, data: Op( Expression { span: Span { - [salsa id]: Id(0), + [salsa id]: Id(400), start: 6, end: 7, }, @@ -772,14 +772,14 @@ fn parse_precedence() { Add, Expression { span: Span { - [salsa id]: Id(5), + [salsa id]: Id(405), start: 10, end: 15, }, data: Op( Expression { span: Span { - [salsa id]: Id(2), + [salsa id]: Id(402), start: 10, end: 11, }, @@ -790,7 +790,7 @@ fn parse_precedence() { Multiply, Expression { span: Span { - [salsa id]: Id(4), + [salsa id]: Id(404), start: 14, end: 15, }, @@ -805,7 +805,7 @@ fn parse_precedence() { Add, Expression { span: Span { - [salsa id]: Id(8), + [salsa id]: Id(408), start: 18, end: 19, }, diff --git a/examples/calc/type_check.rs b/examples/calc/type_check.rs index 5276c48..d73a552 100644 --- a/examples/calc/type_check.rs +++ b/examples/calc/type_check.rs @@ -268,7 +268,7 @@ fn fix_bad_variable_in_function() { expect![[r#" [ "Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: parse_statements(Id(0)) } }", - "Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: type_check_function(Id(1400)) } }", + "Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: type_check_function(Id(1800)) } }", ] "#]], )], diff --git a/src/input.rs b/src/input.rs index d53efcb..47ea6d7 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,16 +1,11 @@ -use std::{ - any::Any, - fmt, - ops::DerefMut, - sync::atomic::{AtomicU32, Ordering}, -}; +use std::{any::Any, fmt, ops::DerefMut}; pub mod input_field; pub mod setter; -mod struct_map; +use crossbeam::atomic::AtomicCell; use input_field::FieldIngredientImpl; -use struct_map::StructMap; +use parking_lot::Mutex; use crate::{ cycle::CycleRecoveryStrategy, @@ -18,7 +13,8 @@ use crate::{ ingredient::{fmt_index, Ingredient}, key::{DatabaseKeyIndex, DependencyIndex}, plumbing::{Jar, JarAux, Stamp}, - zalsa::IngredientIndex, + table::{memo::MemoTable, Slot, Table}, + zalsa::{IngredientIndex, Zalsa}, zalsa_local::QueryOrigin, Database, Durability, Id, Revision, Runtime, }; @@ -57,15 +53,10 @@ impl Jar for JarImpl { struct_index: crate::zalsa::IngredientIndex, ) -> Vec> { let struct_ingredient: IngredientImpl = IngredientImpl::new(struct_index); - let struct_map = struct_ingredient.struct_map.clone(); std::iter::once(Box::new(struct_ingredient) as _) .chain((0..C::FIELD_DEBUG_NAMES.len()).map(|field_index| { - Box::new(FieldIngredientImpl::new( - struct_index, - field_index, - struct_map.clone(), - )) as _ + Box::new(>::new(struct_index, field_index)) as _ })) .collect() } @@ -73,8 +64,8 @@ impl Jar for JarImpl { pub struct IngredientImpl { ingredient_index: IngredientIndex, - counter: AtomicU32, - struct_map: StructMap, + singleton_index: AtomicCell>, + singleton_lock: Mutex<()>, _phantom: std::marker::PhantomData, } @@ -82,12 +73,20 @@ impl IngredientImpl { pub fn new(index: IngredientIndex) -> Self { Self { ingredient_index: index, - counter: Default::default(), - struct_map: StructMap::new(), + singleton_index: AtomicCell::new(None), + singleton_lock: Default::default(), _phantom: std::marker::PhantomData, } } + fn data<'db>(zalsa: &'db Zalsa, id: Id) -> &'db Value { + zalsa.table().get(id) + } + + fn data_raw<'db>(table: &'db Table, id: Id) -> *mut Value { + table.get_raw(id) + } + pub fn database_key_index(&self, id: C::Struct) -> DatabaseKeyIndex { DatabaseKeyIndex { ingredient_index: self.ingredient_index, @@ -95,19 +94,36 @@ impl IngredientImpl { } } - pub fn new_input(&self, fields: C::Fields, stamps: C::Stamps) -> C::Struct { + pub fn new_input(&self, db: &dyn Database, fields: C::Fields, stamps: C::Stamps) -> C::Struct { + let (zalsa, zalsa_local) = db.zalsas(); + // If declared as a singleton, only allow a single instance - if C::IS_SINGLETON && self.counter.load(Ordering::Relaxed) >= 1 { - panic!("singleton struct may not be duplicated"); + let guard = if C::IS_SINGLETON { + let guard = self.singleton_lock.lock(); + if self.singleton_index.load().is_some() { + panic!("singleton struct may not be duplicated"); + } + Some(guard) + } else { + None + }; + + let id = zalsa_local.allocate( + zalsa.table(), + self.ingredient_index, + Value:: { + fields, + stamps, + memos: Default::default(), + }, + ); + + if C::IS_SINGLETON { + self.singleton_index.store(Some(id)); + drop(guard); } - let next_id = Id::from_u32(self.counter.fetch_add(1, Ordering::Relaxed)); - let value = Value { - id: next_id, - fields, - stamps, - }; - self.struct_map.insert(value) + FromId::from_id(id) } /// Change the value of the field `field_index` to a new value. @@ -128,7 +144,12 @@ impl IngredientImpl { setter: impl FnOnce(&mut C::Fields) -> R, ) -> R { let id: Id = id.as_id(); - let mut r = self.struct_map.update(id); + let r = Self::data_raw(runtime.table(), id); + + // SAFETY: We hold `&mut` on the runtime so no `&`-references can be active. + // Also, we don't access any other data from the table while `r` is active. + let r = unsafe { &mut *r }; + let stamp = &mut r.stamps[field_index]; if stamp.durability != Durability::LOW { @@ -146,7 +167,7 @@ impl IngredientImpl { C::IS_SINGLETON, "get_singleton_input invoked on a non-singleton" ); - (self.counter.load(Ordering::Relaxed) > 0).then(|| C::Struct::from_id(Id::from_u32(0))) + self.singleton_index.load().map(FromId::from_id) } /// Access field of an input. @@ -158,10 +179,10 @@ impl IngredientImpl { id: C::Struct, field_index: usize, ) -> &'db C::Fields { - let zalsa_local = db.zalsa_local(); + let (zalsa, zalsa_local) = db.zalsas(); let field_ingredient_index = self.ingredient_index.successor(field_index); let id = id.as_id(); - let value = self.struct_map.get(id); + let value = Self::data(zalsa, id); let stamp = &value.stamps[field_index]; zalsa_local.report_tracked_read( DependencyIndex { @@ -176,9 +197,10 @@ impl IngredientImpl { /// Peek at the field values without recording any read dependency. /// Used for debug printouts. - pub fn leak_fields(&self, id: C::Struct) -> &C::Fields { + pub fn leak_fields<'db>(&'db self, db: &'db dyn Database, id: C::Struct) -> &'db C::Fields { + let zalsa = db.zalsa(); let id = id.as_id(); - let value = self.struct_map.get(id); + let value = Self::data(zalsa, id); &value.fields } } @@ -267,17 +289,26 @@ pub struct Value where C: Configuration, { - /// The id of this struct in the ingredient. - id: Id, - /// Fields of this input struct. They can change across revisions, /// but they do not change within a particular revision. fields: C::Fields, /// The revision and durability information for each field: when did this field last change. stamps: C::Stamps, + + /// Memos + memos: MemoTable, } pub trait HasBuilder { type Builder; } + +impl Slot for Value +where + C: Configuration, +{ + fn memos(&self) -> Option<&crate::table::memo::MemoTable> { + Some(&self.memos) + } +} diff --git a/src/input/input_field.rs b/src/input/input_field.rs index 69683bf..83c0252 100644 --- a/src/input/input_field.rs +++ b/src/input/input_field.rs @@ -5,8 +5,9 @@ use crate::zalsa::IngredientIndex; use crate::zalsa_local::QueryOrigin; use crate::{Database, DatabaseKeyIndex, Id, Revision}; use std::fmt; +use std::marker::PhantomData; -use super::struct_map::StructMap; +use super::{IngredientImpl, Value}; /// Ingredient used to represent the fields of a `#[salsa::input]`. /// @@ -20,22 +21,18 @@ use super::struct_map::StructMap; pub struct FieldIngredientImpl { index: IngredientIndex, field_index: usize, - struct_map: StructMap, + phantom: PhantomData Value>, } impl FieldIngredientImpl where C: Configuration, { - pub(super) fn new( - struct_index: IngredientIndex, - field_index: usize, - struct_map: StructMap, - ) -> Self { + pub(super) fn new(struct_index: IngredientIndex, field_index: usize) -> Self { Self { index: struct_index.successor(field_index), field_index, - struct_map, + phantom: PhantomData, } } } @@ -54,12 +51,14 @@ where fn maybe_changed_after( &self, - _db: &dyn Database, + db: &dyn Database, input: Option, revision: Revision, ) -> bool { + let zalsa = db.zalsa(); let input = input.unwrap(); - self.struct_map.get(input).stamps[self.field_index].changed_at > revision + let value = >::data(zalsa, input); + value.stamps[self.field_index].changed_at > revision } fn origin(&self, _db: &dyn Database, _key_index: Id) -> Option { diff --git a/src/input/struct_map.rs b/src/input/struct_map.rs deleted file mode 100644 index c44c857..0000000 --- a/src/input/struct_map.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::{ops::DerefMut, sync::Arc}; - -use dashmap::mapref::one::RefMut; - -use crate::{alloc::Alloc, hash::FxDashMap, id::FromId, Id}; - -use super::{Configuration, Value}; - -/// Maps an input id to its data. -/// -/// Input structs can only be mutated by a call to a setter with an `&mut` -/// reference to the database, and therefore cannot be mutated during a tracked -/// function or in parallel. -/// -/// However for on-demand inputs to work the fields must be able to be set via -/// a shared reference, so some locking is required. -/// -/// Altogether this makes the implementation somewhat simpler than tracked -/// structs. -pub(crate) struct StructMap -where - C: Configuration, -{ - map: Arc>>>, -} - -impl Clone for StructMap { - fn clone(&self) -> Self { - Self { - map: self.map.clone(), - } - } -} - -impl StructMap -where - C: Configuration, -{ - pub fn new() -> Self { - Self { - map: Arc::new(FxDashMap::default()), - } - } - - /// Insert the given tracked struct value into the map. - /// - /// # Panics - /// - /// * If value with same `value.id` is already present in the map. - /// * If value not created in current revision. - pub fn insert(&self, value: Value) -> C::Struct { - let id = value.id; - let boxed_value = Alloc::new(value); - let old_value = self.map.insert(id, boxed_value); - assert!(old_value.is_none()); // ...strictly speaking we probably need to abort here - C::Struct::from_id(id) - } - - /// Get immutable access to the data for `id` -- this holds a write lock for the duration - /// of the returned value. - /// - /// # Panics - /// - /// * If the value is not present in the map. - /// * If the value is already updated in this revision. - pub fn get(&self, id: Id) -> &Value { - /// More limited wrapper around transmute that copies lifetime from `a` to `b`. - /// - /// # Safety condition - /// - /// `b` must be owned by `a` - unsafe fn transmute_lifetime<'a, A, B>(_a: &'a A, b: &B) -> &'a B { - std::mem::transmute(b) - } - unsafe { transmute_lifetime(self, self.map.get(&id).unwrap().as_ref()) } - } - - /// Get mutable access to the data for `id` -- this holds a write lock for the duration - /// of the returned value. - /// - /// # Panics - /// - /// * If the value is not present in the map. - /// * If the value is already updated in this revision. - pub fn update(&mut self, id: Id) -> impl DerefMut> + '_ { - RefMut::map(self.map.get_mut(&id).unwrap(), |v| unsafe { v.as_mut() }) - } -} diff --git a/src/runtime.rs b/src/runtime.rs index 02f8a1d..740f420 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -9,8 +9,8 @@ use parking_lot::Mutex; use crate::{ active_query::ActiveQuery, cycle::CycleRecoveryStrategy, durability::Durability, - key::DatabaseKeyIndex, revision::AtomicRevision, zalsa_local::ZalsaLocal, Cancelled, Cycle, - Database, Event, EventKind, Revision, + key::DatabaseKeyIndex, revision::AtomicRevision, table::Table, zalsa_local::ZalsaLocal, + Cancelled, Cycle, Database, Event, EventKind, Revision, }; use self::dependency_graph::DependencyGraph; @@ -40,6 +40,9 @@ pub struct Runtime { /// The dependency graph tracks which runtimes are blocked on one /// another, waiting for queries to terminate. dependency_graph: Mutex, + + /// Data for instances + table: Table, } #[derive(Clone, Debug)] @@ -84,6 +87,7 @@ impl Default for Runtime { next_id: AtomicUsize::new(1), revision_canceled: Default::default(), dependency_graph: Default::default(), + table: Default::default(), } } } @@ -134,6 +138,14 @@ impl Runtime { self.revision_canceled.store(true); } + pub(crate) fn table(&self) -> &Table { + &self.table + } + + pub(crate) fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + /// Increments the "current revision" counter and clears /// the cancellation flag. /// diff --git a/src/table.rs b/src/table.rs index c1c979a..0aa6e9e 100644 --- a/src/table.rs +++ b/src/table.rs @@ -9,7 +9,10 @@ use crossbeam::atomic::AtomicCell; use memo::MemoTable; use parking_lot::Mutex; -use crate::{zalsa::transmute_data_ptr, Id, IngredientIndex}; +use crate::{ + zalsa::{transmute_data_ptr, transmute_data_ptr_mut}, + Id, IngredientIndex, +}; pub(crate) mod memo; @@ -75,22 +78,62 @@ impl Default for Table { } impl Table { + /// Get a reference to the data for `id`, which must have been allocated from this table with type `T`. + /// + /// # Panics + /// + /// If `id` is out of bounds or the does not have the type `T`. pub fn get(&self, id: Id) -> &T { let (page, slot) = split_id(id); let page_ref = self.page::(page); page_ref.get(slot) } + /// Get a mutable reference to the data for `id`, which must have been allocated from this table with type `T`. + /// + /// # Panics + /// + /// If `id` is out of bounds or the does not have the type `T`. + pub fn get_mut(&mut self, id: Id) -> &mut T { + let (page, slot) = split_id(id); + let page_ref = self.page_mut::(page); + page_ref.get_mut(slot) + } + + /// Get a raw pointer to the data for `id`, which must have been allocated from this table. + /// + /// # Panics + /// + /// If `id` is out of bounds or the does not have the type `T`. + /// + /// # Safety + /// + /// See [`Page::get_raw`][]. pub fn get_raw(&self, id: Id) -> *mut T { let (page, slot) = split_id(id); let page_ref = self.page::(page); page_ref.get_raw(slot) } + /// Gets a reference to the page which has slots of type `T` + /// + /// # Panics + /// + /// If `page` is out of bounds or the type `T` is incorrect. pub fn page(&self, page: PageIndex) -> &Page { self.pages[page.0].assert_type::>() } + /// Gets a mutable reference to the page which has slots of type `T` + /// + /// # Panics + /// + /// If `page` is out of bounds or the type `T` is incorrect. + fn page_mut(&mut self, page: PageIndex) -> &mut Page { + self.pages[page.0].assert_type_mut::>() + } + + /// Allocate a new page for the given ingredient and with slots of type `T` pub fn push_page(&self, ingredient: IngredientIndex) -> PageIndex { let page = Box::new(>::new(ingredient)); PageIndex(self.pages.push(page)) @@ -125,13 +168,36 @@ impl Page { ); } + /// Returns a reference to the given slot. + /// + /// # Panics + /// + /// If slot is out of bounds pub(crate) fn get(&self, slot: SlotIndex) -> &T { self.check_bounds(slot); unsafe { &*self.data[slot.0].get() } } + /// Returns a mut reference to the given slot. + /// + /// # Panics + /// + /// If slot is out of bounds + pub(crate) fn get_mut(&mut self, slot: SlotIndex) -> &mut T { + self.check_bounds(slot); + self.data[slot.0].get_mut() + } + /// Returns a raw pointer to the given slot. - /// Reads/writes must be coordinated properly with calls to `get`. + /// + /// # Panics + /// + /// If slot is out of bounds + /// + /// # Safety + /// + /// Safe to call, but reads/writes through this pointer must be coordinated + /// properly with calls to [`get`](`Self::get`) and [`get_mut`](`Self::get_mut`). pub(crate) fn get_raw(&self, slot: SlotIndex) -> *mut T { self.check_bounds(slot); self.data[slot.0].get() @@ -183,7 +249,7 @@ impl Drop for Page { impl dyn TablePage { fn assert_type(&self) -> &T { assert_eq!( - self.type_id(), + Any::type_id(self), TypeId::of::(), "page has hidden type `{:?}` but `{:?}` was expected", self.hidden_type_name(), @@ -193,6 +259,19 @@ impl dyn TablePage { // SAFETY: Assertion above unsafe { transmute_data_ptr::(self) } } + + fn assert_type_mut(&mut self) -> &mut T { + assert_eq!( + Any::type_id(self), + TypeId::of::(), + "page has hidden type `{:?}` but `{:?}` was expected", + self.hidden_type_name(), + std::any::type_name::(), + ); + + // SAFETY: Assertion above + unsafe { transmute_data_ptr_mut::(self) } + } } fn make_id(page: PageIndex, slot: SlotIndex) -> Id { diff --git a/src/zalsa.rs b/src/zalsa.rs index a6d7254..9b05bb0 100644 --- a/src/zalsa.rs +++ b/src/zalsa.rs @@ -132,9 +132,6 @@ pub struct Zalsa { /// The runtime for this particular salsa database handle. /// Each handle gets its own runtime, but the runtimes have shared state between them. runtime: Runtime, - - /// Data for instances - table: Table, } impl Zalsa { @@ -146,7 +143,6 @@ impl Zalsa { ingredients_vec: AppendOnlyVec::new(), ingredients_requiring_reset: AppendOnlyVec::new(), runtime: Runtime::default(), - table: Table::default(), memo_ingredient_count: AtomicCell::new(0), } } @@ -160,7 +156,7 @@ impl Zalsa { } pub(crate) fn table(&self) -> &Table { - &self.table + self.runtime.table() } /// **NOT SEMVER STABLE** @@ -349,3 +345,14 @@ pub(crate) unsafe fn transmute_data_ptr(t: &T) -> &U { let u: *const U = t as *const U; unsafe { &*u } } + +/// Given a wide pointer `T`, extracts the data pointer (typed as `U`). +/// +/// # Safety requirement +/// +/// `U` must be correct type for the data pointer. +pub(crate) unsafe fn transmute_data_ptr_mut(t: &mut T) -> &mut U { + let t: *mut T = t; + let u: *mut U = t as *mut U; + unsafe { &mut *u } +} diff --git a/tests/debug.rs b/tests/debug.rs index 194c651..3c2c896 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -30,7 +30,7 @@ fn input() { // debug includes all fields let actual = format!("{complex_struct:?}"); - let expected = expect![[r#"ComplexStruct { [salsa id]: Id(0), my_input: MyInput { [salsa id]: Id(0), field: 22 }, not_salsa: NotSalsa { field: "it's salsa time" } }"#]]; + let expected = expect![[r#"ComplexStruct { [salsa id]: Id(400), my_input: MyInput { [salsa id]: Id(0), field: 22 }, not_salsa: NotSalsa { field: "it's salsa time" } }"#]]; expected.assert_eq(&actual); }) } diff --git a/tests/deletion-cascade.rs b/tests/deletion-cascade.rs index 8996933..7b3b221 100644 --- a/tests/deletion-cascade.rs +++ b/tests/deletion-cascade.rs @@ -80,11 +80,11 @@ fn basic() { db.assert_logs(expect![[r#" [ "intermediate_result(MyInput { [salsa id]: Id(0), field: 2 })", - "salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(2)) })", - "salsa_event(DidDiscard { key: MyTracked(Id(2)) })", - "salsa_event(DidDiscard { key: contribution_from_struct(Id(2)) })", - "salsa_event(DidDiscard { key: MyTracked(Id(5)) })", - "salsa_event(DidDiscard { key: copy_field(Id(5)) })", + "salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(402)) })", + "salsa_event(DidDiscard { key: MyTracked(Id(402)) })", + "salsa_event(DidDiscard { key: contribution_from_struct(Id(402)) })", + "salsa_event(DidDiscard { key: MyTracked(Id(405)) })", + "salsa_event(DidDiscard { key: copy_field(Id(405)) })", "final_result(MyInput { [salsa id]: Id(0), field: 2 })", ]"#]]); } diff --git a/tests/deletion.rs b/tests/deletion.rs index c5c286e..0aef146 100644 --- a/tests/deletion.rs +++ b/tests/deletion.rs @@ -67,9 +67,9 @@ fn basic() { db.assert_logs(expect![[r#" [ "intermediate_result(MyInput { [salsa id]: Id(0), field: 2 })", - "salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(2)) })", - "salsa_event(DidDiscard { key: MyTracked(Id(2)) })", - "salsa_event(DidDiscard { key: contribution_from_struct(Id(2)) })", + "salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(402)) })", + "salsa_event(DidDiscard { key: MyTracked(Id(402)) })", + "salsa_event(DidDiscard { key: contribution_from_struct(Id(402)) })", "final_result(MyInput { [salsa id]: Id(0), field: 2 })", ]"#]]); } diff --git a/tests/preverify-struct-with-leaked-data-2.rs b/tests/preverify-struct-with-leaked-data-2.rs index 1dc6812..5632e99 100644 --- a/tests/preverify-struct-with-leaked-data-2.rs +++ b/tests/preverify-struct-with-leaked-data-2.rs @@ -64,7 +64,7 @@ fn test_leaked_inputs_ignored() { "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", - "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(400)) } }", + "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(800)) } }", ]"#]]); assert_eq!(result_in_rev_1, (0, 0)); @@ -82,7 +82,7 @@ fn test_leaked_inputs_ignored() { "Event { thread_id: ThreadId(2), kind: DidSetCancellationFlag }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", - "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(400)) } }", + "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(800)) } }", "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", ]"#]]); diff --git a/tests/preverify-struct-with-leaked-data.rs b/tests/preverify-struct-with-leaked-data.rs index 448fad9..5bb1fab 100644 --- a/tests/preverify-struct-with-leaked-data.rs +++ b/tests/preverify-struct-with-leaked-data.rs @@ -61,7 +61,7 @@ fn test_leaked_inputs_ignored() { "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", - "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(0)) } }", + "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(400)) } }", ]"#]]); assert_eq!(result_in_rev_1, (0, 0)); @@ -79,7 +79,7 @@ fn test_leaked_inputs_ignored() { "Event { thread_id: ThreadId(2), kind: DidSetCancellationFlag }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", - "Event { thread_id: ThreadId(2), kind: DidValidateMemoizedValue { database_key: counter_field(Id(0)) } }", + "Event { thread_id: ThreadId(2), kind: DidValidateMemoizedValue { database_key: counter_field(Id(400)) } }", "Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }", "Event { thread_id: ThreadId(2), kind: WillCheckCancellation }", ]"#]]); diff --git a/tests/tracked-struct-value-field-bad-eq.rs b/tests/tracked-struct-value-field-bad-eq.rs index 2e1aa81..f7444fa 100644 --- a/tests/tracked-struct-value-field-bad-eq.rs +++ b/tests/tracked-struct-value-field-bad-eq.rs @@ -64,7 +64,7 @@ fn execute() { [ "salsa_event(WillExecute { database_key: the_fn(Id(0)) })", "salsa_event(WillExecute { database_key: make_tracked_struct(Id(0)) })", - "salsa_event(WillExecute { database_key: read_tracked_struct(Id(0)) })", + "salsa_event(WillExecute { database_key: read_tracked_struct(Id(400)) })", ]"#]]); // Update the input to `false` and re-execute. @@ -79,7 +79,7 @@ fn execute() { db.assert_logs(expect![[r#" [ "salsa_event(WillExecute { database_key: make_tracked_struct(Id(0)) })", - "salsa_event(DidValidateMemoizedValue { database_key: read_tracked_struct(Id(0)) })", + "salsa_event(DidValidateMemoizedValue { database_key: read_tracked_struct(Id(400)) })", "salsa_event(DidValidateMemoizedValue { database_key: the_fn(Id(0)) })", ]"#]]); } diff --git a/tests/tracked_with_struct_db.rs b/tests/tracked_with_struct_db.rs index 236eded..6c6d2ab 100644 --- a/tests/tracked_with_struct_db.rs +++ b/tests/tracked_with_struct_db.rs @@ -36,14 +36,14 @@ fn execute() { let t1 = create_tracked_list(db, input); expect_test::expect![[r#" MyTracked { - [salsa id]: Id(1), + [salsa id]: Id(401), data: MyInput { [salsa id]: Id(0), field: "foo", }, next: Next( MyTracked { - [salsa id]: Id(0), + [salsa id]: Id(400), data: MyInput { [salsa id]: Id(0), field: "foo",