introduce Slot trait

This commit is contained in:
Niko Matsakis 2024-08-12 08:29:20 +03:00
parent 33a99da476
commit db8d64faa6
4 changed files with 34 additions and 45 deletions

View file

@ -7,6 +7,7 @@ use crate::id::AsId;
use crate::ingredient::fmt_index;
use crate::key::DependencyIndex;
use crate::plumbing::Jar;
use crate::table::Slot;
use crate::zalsa::IngredientIndex;
use crate::zalsa_local::QueryOrigin;
use crate::{Database, DatabaseKeyIndex, Id};
@ -19,7 +20,7 @@ pub trait Configuration: Sized + 'static {
const DEBUG_NAME: &'static str;
/// The type of data being interned
type Data<'db>: InternedData + Send + Sync;
type Data<'db>: InternedData;
/// The end user struct
type Struct<'db>: Copy;
@ -36,8 +37,8 @@ pub trait Configuration: Sized + 'static {
fn deref_struct(s: Self::Struct<'_>) -> Id;
}
pub trait InternedData: Sized + Eq + Hash + Clone {}
impl<T: Eq + Hash + Clone> InternedData for T {}
pub trait InternedData: Sized + Eq + Hash + Clone + Sync + Send {}
impl<T: Eq + Hash + Clone + Sync + Send> InternedData for T {}
pub struct JarImpl<C: Configuration> {
phantom: PhantomData<C>,
@ -67,8 +68,7 @@ pub struct Value<C>
where
C: Configuration,
{
id: Id,
fields: C::Data<'static>,
data: C::Data<'static>,
}
impl<C: Configuration> Default for JarImpl<C> {
@ -147,7 +147,13 @@ where
dashmap::mapref::entry::Entry::Vacant(entry) => {
let zalsa = db.zalsa();
let table = zalsa.table();
let next_id = zalsa_local.allocate(table, self.ingredient_index, internal_data);
let next_id = zalsa_local.allocate(
table,
self.ingredient_index,
Value::<C> {
data: internal_data,
},
);
entry.insert(next_id);
C::struct_from_id(next_id)
}
@ -158,8 +164,8 @@ where
/// Rarely used since end-users generally carry a struct with a pointer directly
/// to the interned item.
pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Data<'db> {
let internal_data = db.zalsa().table().get::<C::Data<'static>>(id);
unsafe { self.from_internal_data(internal_data) }
let internal_data = db.zalsa().table().get::<Value<C>>(id);
unsafe { self.from_internal_data(&internal_data.data) }
}
/// Lookup the fields from an interned struct.
@ -259,26 +265,4 @@ where
}
}
impl<C> Value<C>
where
C: Configuration,
{
pub fn data(&self) -> &C::Data<'_> {
// SAFETY: The lifetime of `self` is tied to the interning ingredient;
// we never remove data without an `&mut self` access to the interning ingredient.
unsafe { self.to_self_ref(&self.fields) }
}
unsafe fn to_self_ref<'db>(&'db self, fields: &'db C::Data<'static>) -> &'db C::Data<'db> {
unsafe { std::mem::transmute(fields) }
}
}
impl<C> AsId for Value<C>
where
C: Configuration,
{
fn as_id(&self) -> Id {
self.id
}
}
impl<C> Slot for Value<C> where C: Configuration {}

View file

@ -6,6 +6,7 @@ use std::{
use append_only_vec::AppendOnlyVec;
use crossbeam::atomic::AtomicCell;
use memo::MemoTable;
use parking_lot::Mutex;
use crate::{zalsa::transmute_data_ptr, Id, IngredientIndex};
@ -24,7 +25,7 @@ pub(crate) trait TablePage: Any + Send + Sync {
fn hidden_type_name(&self) -> &'static str;
}
pub(crate) struct Page<T: Any + Send + Sync> {
pub(crate) struct Page<T: Slot> {
/// The ingredient for elements on this page.
#[allow(dead_code)] // pretty sure we'll need this
ingredient: IngredientIndex,
@ -47,11 +48,13 @@ pub(crate) struct Page<T: Any + Send + Sync> {
data: Vec<UnsafeCell<T>>,
}
unsafe impl<T: Any + Send + Sync> Send for Page<T> {}
pub(crate) trait Slot: Any + Send + Sync {}
unsafe impl<T: Any + Send + Sync> Sync for Page<T> {}
unsafe impl<T: Slot> Send for Page<T> {}
impl<T: Any + Send + Sync> RefUnwindSafe for Page<T> {}
unsafe impl<T: Slot> Sync for Page<T> {}
impl<T: Slot> RefUnwindSafe for Page<T> {}
#[derive(Copy, Clone)]
pub struct PageIndex(usize);
@ -68,29 +71,29 @@ impl Default for Table {
}
impl Table {
pub fn get<T: Any + Send + Sync>(&self, id: Id) -> &T {
pub fn get<T: Slot>(&self, id: Id) -> &T {
let (page, slot) = split_id(id);
let page_ref = self.page::<T>(page);
page_ref.get(slot)
}
pub fn get_raw<T: Any + Send + Sync>(&self, id: Id) -> *mut T {
pub fn get_raw<T: Slot>(&self, id: Id) -> *mut T {
let (page, slot) = split_id(id);
let page_ref = self.page::<T>(page);
page_ref.get_raw(slot)
}
pub fn page<T: Any + Send + Sync>(&self, page: PageIndex) -> &Page<T> {
pub fn page<T: Slot>(&self, page: PageIndex) -> &Page<T> {
self.pages[page.0].assert_type::<Page<T>>()
}
pub fn push_page<T: Any + Send + Sync>(&self, ingredient: IngredientIndex) -> PageIndex {
pub fn push_page<T: Slot>(&self, ingredient: IngredientIndex) -> PageIndex {
let page = Box::new(<Page<T>>::new(ingredient));
PageIndex(self.pages.push(page))
}
}
impl<T: Any + Send + Sync> Page<T> {
impl<T: Slot> Page<T> {
fn new(ingredient: IngredientIndex) -> Self {
let mut data = Vec::with_capacity(PAGE_LEN);
unsafe {
@ -137,13 +140,13 @@ impl<T: Any + Send + Sync> Page<T> {
}
}
impl<T: Any + Send + Sync> TablePage for Page<T> {
impl<T: Slot> TablePage for Page<T> {
fn hidden_type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
}
impl<T: Any + Send + Sync> Drop for Page<T> {
impl<T: Slot> Drop for Page<T> {
fn drop(&mut self) {
// Free `self.data` and the data within: to do this, we swap it out with an empty vector
// and then convert it from a `Vec<UnsafeCell<T>>` with partially uninitialized values

View file

@ -11,7 +11,7 @@ use crate::{
plumbing::ZalsaLocal,
runtime::StampedValue,
salsa_struct::SalsaStructInDb,
table::Table,
table::{Slot, Table},
zalsa::{IngredientIndex, Zalsa},
zalsa_local::QueryOrigin,
Database, Durability, Event, Id, Revision,
@ -634,3 +634,5 @@ where
.finish()
}
}
impl<C> Slot for Value<C> where C: Configuration {}

View file

@ -7,6 +7,7 @@ use crate::key::DatabaseKeyIndex;
use crate::key::DependencyIndex;
use crate::runtime::StampedValue;
use crate::table::PageIndex;
use crate::table::Slot;
use crate::table::Table;
use crate::tracked_struct::Disambiguator;
use crate::tracked_struct::KeyStruct;
@ -18,7 +19,6 @@ use crate::Event;
use crate::EventKind;
use crate::Id;
use crate::Revision;
use std::any::Any;
use std::cell::RefCell;
use std::sync::Arc;
@ -54,7 +54,7 @@ impl ZalsaLocal {
/// Allocate a new id in `table` for the given ingredient
/// storing `value`. Remembers the most recent page from this
/// thread and attempts to reuse it.
pub(crate) fn allocate<'t, T: Any + Send + Sync>(
pub(crate) fn allocate<'t, T: Slot>(
&self,
table: &Table,
ingredient: IngredientIndex,