salsa/src/input.rs
2024-07-19 05:55:40 -04:00

269 lines
7.9 KiB
Rust

use std::{
any::Any,
fmt,
ops::DerefMut,
sync::atomic::{AtomicU32, Ordering},
};
pub mod input_field;
pub mod setter;
mod struct_map;
use input_field::FieldIngredientImpl;
use struct_map::StructMap;
use crate::{
cycle::CycleRecoveryStrategy,
id::{AsId, FromId},
ingredient::{fmt_index, Ingredient, IngredientRequiresReset},
key::{DatabaseKeyIndex, DependencyIndex},
plumbing::{Jar, Stamp},
runtime::{local_state::QueryOrigin, Runtime},
storage::IngredientIndex,
Database, Durability, Id, Revision,
};
pub trait Configuration: Any {
const DEBUG_NAME: &'static str;
const FIELD_DEBUG_NAMES: &'static [&'static str];
const IS_SINGLETON: bool;
/// The input struct (which wraps an `Id`)
type Struct: FromId + 'static + Send + Sync;
/// A (possibly empty) tuple of the fields for this struct.
type Fields: Send + Sync;
/// A array of [`StampedValue<()>`](`StampedValue`) tuples, one per each of the value fields.
type Stamps: Send + Sync + DerefMut<Target = [Stamp]>;
}
pub struct JarImpl<C: Configuration> {
_phantom: std::marker::PhantomData<C>,
}
impl<C: Configuration> Default for JarImpl<C> {
fn default() -> Self {
Self {
_phantom: Default::default(),
}
}
}
impl<C: Configuration> Jar for JarImpl<C> {
fn create_ingredients(
&self,
struct_index: crate::storage::IngredientIndex,
) -> Vec<Box<dyn Ingredient>> {
let struct_ingredient: IngredientImpl<C> = 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 _
}))
.collect()
}
}
pub struct IngredientImpl<C: Configuration> {
ingredient_index: IngredientIndex,
counter: AtomicU32,
struct_map: StructMap<C>,
_phantom: std::marker::PhantomData<C::Struct>,
}
impl<C: Configuration> IngredientImpl<C> {
pub fn new(index: IngredientIndex) -> Self {
Self {
ingredient_index: index,
counter: Default::default(),
struct_map: StructMap::new(),
_phantom: std::marker::PhantomData,
}
}
pub fn database_key_index(&self, id: C::Struct) -> DatabaseKeyIndex {
DatabaseKeyIndex {
ingredient_index: self.ingredient_index,
key_index: id.as_id(),
}
}
pub fn new_input(&self, fields: C::Fields, stamps: C::Stamps) -> C::Struct {
// 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 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)
}
/// Change the value of the field `field_index` to a new value.
///
/// # Parameters
///
/// * `runtime`, the salsa runtiem
/// * `id`, id of the input struct
/// * `field_index`, index of the field that will be changed
/// * `durability`, durability of the new value
/// * `setter`, function that modifies the fields tuple; should only modify the element for `field_index`
pub fn set_field<R>(
&mut self,
runtime: &mut Runtime,
id: C::Struct,
field_index: usize,
durability: Durability,
setter: impl FnOnce(&mut C::Fields) -> R,
) -> R {
let revision = runtime.current_revision();
let id: Id = id.as_id();
let mut r = self.struct_map.update(id);
let stamp = &mut r.stamps[field_index];
stamp.durability = durability;
stamp.changed_at = revision;
setter(&mut r.fields)
}
/// Get the singleton input previously created (if any).
pub fn get_singleton_input(&self) -> Option<C::Struct> {
assert!(
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)))
}
/// Access field of an input.
/// Note that this function returns the entire tuple of value fields.
/// The caller is responible for selecting the appropriate element.
pub fn field<'db>(
&'db self,
runtime: &'db Runtime,
id: C::Struct,
field_index: usize,
) -> &'db C::Fields {
let field_ingredient_index = self.ingredient_index.successor(field_index);
let id = id.as_id();
let value = self.struct_map.get(id);
let stamp = &value.stamps[field_index];
runtime.report_tracked_read(
DependencyIndex {
ingredient_index: field_ingredient_index,
key_index: Some(id),
},
stamp.durability,
stamp.changed_at,
);
&value.fields
}
/// Peek at the field values without recording any read dependency.
/// Used for debug printouts.
pub fn leak_fields(&self, id: C::Struct) -> &C::Fields {
let id = id.as_id();
let value = self.struct_map.get(id);
&value.fields
}
}
impl<C: Configuration> Ingredient for IngredientImpl<C> {
fn ingredient_index(&self) -> IngredientIndex {
self.ingredient_index
}
fn maybe_changed_after(
&self,
_db: &dyn Database,
_input: Option<Id>,
_revision: Revision,
) -> bool {
// Input ingredients are just a counter, they store no data, they are immortal.
// Their *fields* are stored in function ingredients elsewhere.
false
}
fn cycle_recovery_strategy(&self) -> CycleRecoveryStrategy {
CycleRecoveryStrategy::Panic
}
fn origin(&self, _key_index: Id) -> Option<QueryOrigin> {
None
}
fn mark_validated_output(
&self,
_db: &dyn Database,
executor: DatabaseKeyIndex,
output_key: Option<Id>,
) {
unreachable!(
"mark_validated_output({:?}, {:?}): input cannot be the output of a tracked function",
executor, output_key
);
}
fn remove_stale_output(
&self,
_db: &dyn Database,
executor: DatabaseKeyIndex,
stale_output_key: Option<Id>,
) {
unreachable!(
"remove_stale_output({:?}, {:?}): input cannot be the output of a tracked function",
executor, stale_output_key
);
}
fn reset_for_new_revision(&mut self) {
panic!("unexpected call to `reset_for_new_revision`")
}
fn salsa_struct_deleted(&self, _db: &dyn Database, _id: Id) {
panic!(
"unexpected call: input ingredients do not register for salsa struct deletion events"
);
}
fn fmt_index(&self, index: Option<Id>, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_index(C::DEBUG_NAME, index, fmt)
}
}
impl<C: Configuration> IngredientRequiresReset for IngredientImpl<C> {
const RESET_ON_NEW_REVISION: bool = false;
}
impl<C: Configuration> std::fmt::Debug for IngredientImpl<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(std::any::type_name::<Self>())
.field("index", &self.ingredient_index)
.finish()
}
}
#[derive(Debug)]
pub struct Value<C>
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,
}