Skip to content

Commit

Permalink
WIP new approach with upcasts
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Jul 10, 2024
1 parent fe1b06a commit c84bd1b
Show file tree
Hide file tree
Showing 26 changed files with 655 additions and 1,010 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ description = "A generic framework for on-demand, incrementalized computation (e
[dependencies]
append-only-vec = { git = "https://github.com/nikomatsakis/append-only-vec.git", version = "0.1.4" }
arc-swap = "1.6.0"
boomphf = "0.6.0"
crossbeam = "0.8.1"
dashmap = "5.3.4"
hashlink = "0.8.0"
Expand Down
52 changes: 26 additions & 26 deletions src/accumulator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! Basic test of accumulator functionality.

use std::{any::Any, fmt, marker::PhantomData};
use std::{
any::Any,
fmt::{self, Debug},
marker::PhantomData,
};

use crate::{
cycle::CycleRecoveryStrategy,
Expand All @@ -9,13 +13,13 @@ use crate::{
key::DependencyIndex,
runtime::local_state::QueryOrigin,
storage::IngredientIndex,
Database, DatabaseKeyIndex, Event, EventKind, Revision, Runtime,
Database, DatabaseKeyIndex, Event, EventKind, Id, Revision, Runtime,
};

pub trait Accumulator: Jar {
const DEBUG_NAME: &'static str;

type Data: Clone;
type Data: Clone + Debug;
}

pub struct AccumulatorJar<A: Accumulator> {
Expand All @@ -31,12 +35,7 @@ impl<A: Accumulator> Default for AccumulatorJar<A> {
}

impl<A: Accumulator> Jar for AccumulatorJar<A> {
type DbView = dyn crate::Database;

fn create_ingredients(
&self,
first_index: IngredientIndex,
) -> Vec<Box<dyn Ingredient<DbView = Self::DbView>>> {
fn create_ingredients(&self, first_index: IngredientIndex) -> Vec<Box<dyn Ingredient>> {
vec![Box::new(<AccumulatorIngredient<A>>::new(first_index))]
}
}
Expand All @@ -58,8 +57,8 @@ impl<A: Accumulator> AccumulatorIngredient<A> {
Db: ?Sized + Database,
{
let jar: AccumulatorJar<A> = Default::default();
let index = db.jar_index_by_type_id(jar.type_id())?;
let ingredient = db.ingredient(index).assert_type::<Self>();
let index = db.add_or_lookup_jar_by_type(&jar);
let ingredient = db.lookup_ingredient(index).assert_type::<Self>();
Some(ingredient)
}

Expand Down Expand Up @@ -129,16 +128,14 @@ impl<A: Accumulator> AccumulatorIngredient<A> {
}

impl<A: Accumulator> Ingredient for AccumulatorIngredient<A> {
type DbView = dyn crate::Database;

fn ingredient_index(&self) -> IngredientIndex {
self.index
}

fn maybe_changed_after(
&self,
_db: &Self::DbView,
_input: DependencyIndex,
_db: &dyn Database,
_input: Option<Id>,
_revision: Revision,
) -> bool {
panic!("nothing should ever depend on an accumulator directly")
Expand All @@ -154,7 +151,7 @@ impl<A: Accumulator> Ingredient for AccumulatorIngredient<A> {

fn mark_validated_output(
&self,
db: &Self::DbView,
db: &dyn Database,
executor: DatabaseKeyIndex,
output_key: Option<crate::Id>,
) {
Expand All @@ -168,7 +165,7 @@ impl<A: Accumulator> Ingredient for AccumulatorIngredient<A> {

fn remove_stale_output(
&self,
db: &Self::DbView,
db: &dyn Database,
executor: DatabaseKeyIndex,
stale_output_key: Option<crate::Id>,
) {
Expand All @@ -188,23 +185,26 @@ impl<A: Accumulator> Ingredient for AccumulatorIngredient<A> {
panic!("unexpected reset on accumulator")
}

fn salsa_struct_deleted(&self, _db: &Self::DbView, _id: crate::Id) {
fn salsa_struct_deleted(&self, _db: &dyn Database, _id: crate::Id) {
panic!("unexpected call: accumulator is not registered as a dependent fn");
}

fn fmt_index(&self, index: Option<crate::Id>, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_index(A::DEBUG_NAME, index, fmt)
}

fn upcast_to_raw(&self) -> &dyn crate::ingredient::RawIngredient {
self
}

fn upcast_to_raw_mut(&mut self) -> &mut dyn crate::ingredient::RawIngredient {
self
}
}

impl<A: Accumulator> IngredientRequiresReset for AccumulatorIngredient<A> {
const RESET_ON_NEW_REVISION: bool = false;
}

impl<A> std::fmt::Debug for AccumulatorIngredient<A>
where
A: Accumulator,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(std::any::type_name::<Self>())
.field("index", &self.index)
.finish()
}
}
55 changes: 20 additions & 35 deletions src/cycle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::debug::DebugWithDb;
use crate::{key::DatabaseKeyIndex, Database};
use crate::{database, key::DatabaseKeyIndex, Database};
use std::{panic::AssertUnwindSafe, sync::Arc};

/// Captures the participants of a cycle that occurred when executing a query.
Expand All @@ -17,7 +16,7 @@ use std::{panic::AssertUnwindSafe, sync::Arc};
///
/// You can read more about cycle handling in
/// the [salsa book](https://https://salsa-rs.github.io/salsa/cycles.html).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Cycle {
participants: CycleParticipants,
}
Expand Down Expand Up @@ -59,47 +58,33 @@ impl Cycle {

/// Returns a vector with the debug information for
/// all the participants in the cycle.
pub fn all_participants<DB: ?Sized + Database>(&self, db: &DB) -> Vec<String> {
self.participant_keys()
.map(|d| format!("{:?}", d.debug(db)))
.collect()
pub fn all_participants(&self, _db: &dyn Database) -> Vec<DatabaseKeyIndex> {
self.participant_keys().collect()
}

/// Returns a vector with the debug information for
/// those participants in the cycle that lacked recovery
/// information.
pub fn unexpected_participants<DB: ?Sized + Database>(&self, db: &DB) -> Vec<String> {
pub fn unexpected_participants(&self, db: &dyn Database) -> Vec<DatabaseKeyIndex> {
self.participant_keys()
.filter(|&d| {
db.cycle_recovery_strategy(d.ingredient_index) == CycleRecoveryStrategy::Panic
})
.map(|d| format!("{:?}", d.debug(db)))
.filter(|&d| d.cycle_recovery_strategy(db) == CycleRecoveryStrategy::Panic)
.collect()
}
}

/// Returns a "debug" view onto this strict that can be used to print out information.
pub fn debug<'me, DB: ?Sized + Database>(&'me self, db: &'me DB) -> impl std::fmt::Debug + 'me {
struct UnexpectedCycleDebug<'me> {
c: &'me Cycle,
db: &'me dyn Database,
}

impl<'me> std::fmt::Debug for UnexpectedCycleDebug<'me> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("UnexpectedCycle")
.field("all_participants", &self.c.all_participants(self.db))
.field(
"unexpected_participants",
&self.c.unexpected_participants(self.db),
)
.finish()
}
}

UnexpectedCycleDebug {
c: self,
db: db.as_salsa_database(),
}
impl std::fmt::Debug for Cycle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
database::with_attached_database(|db| {
f.debug_struct("UnexpectedCycle")
.field("all_participants", &self.all_participants(db))
.field("unexpected_participants", &self.unexpected_participants(db))
.finish()
})
.unwrap_or_else(|| {
f.debug_struct("Cycle")
.field("participants", &self.participants)
.finish()
})
}
}

Expand Down
103 changes: 89 additions & 14 deletions src/database.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use std::any::Any;
use std::{any::Any, cell::Cell, ptr::NonNull};

use crate::{
ingredient::Ingredient,
storage::{HasJarsDyn, StorageForView},
DebugWithDb, Durability, Event,
};
use crossbeam::atomic::AtomicCell;
use parking_lot::Mutex;

pub trait Database: HasJarsDyn + AsSalsaDatabase {
use crate::{storage::DatabaseGen, Durability, Event};

pub trait Database: DatabaseGen {
/// This function is invoked at key points in the salsa
/// runtime. It permits the database to be customized and to
/// inject logging or other custom behavior.
///
/// By default, the event is logged at level debug using
/// the standard `log` facade.
fn salsa_event(&self, event: Event) {
log::debug!("salsa_event: {:?}", event.debug(self));
log::debug!("salsa_event: {:?}", event)
}

/// A "synthetic write" causes the system to act *as though* some
Expand All @@ -38,10 +37,19 @@ pub trait Database: HasJarsDyn + AsSalsaDatabase {
}
}

/// The database view trait allows you to define your own views on the database.
/// This lets you add extra context beyond what is stored in the salsa database itself.
pub trait DatabaseView<Dyn: ?Sized + Any>: Database {
fn as_dyn(&self) -> &Dyn;
fn as_dyn_mut(&mut self) -> &mut Dyn;
fn storage_for_view(&self) -> &dyn StorageForView<Dyn>;
/// Registers this database view in the database.
/// This is normally invoked automatically by tracked functions that require a given view.
fn add_view_to_db(&self);
}

impl<Db: Database> DatabaseView<dyn Database> for Db {
fn add_view_to_db(&self) {
let upcasts = self.upcasts_for_self();
upcasts.add::<dyn Database>(|t| t, |t| t);
}
}

/// Indicates a database that also supports parallel query
Expand Down Expand Up @@ -109,9 +117,6 @@ pub trait ParallelDatabase: Database + Send {
/// ```
fn snapshot(&self) -> Snapshot<Self>;
}
pub trait AsSalsaDatabase {
fn as_salsa_database(&self) -> &dyn Database;
}

/// Simple wrapper struct that takes ownership of a database `DB` and
/// only gives `&self` access to it. See [the `snapshot` method][fm]
Expand Down Expand Up @@ -148,3 +153,73 @@ where
&self.db
}
}

thread_local! {
static DATABASE: Cell<AttachedDatabase> = Cell::new(AttachedDatabase::null());
}

/// Access the "attached" database. Returns `None` if no database is attached.
/// Databases are attached with `attach_database`.
pub fn with_attached_database<R>(op: impl FnOnce(&dyn Database) -> R) -> Option<R> {
// SAFETY: We always attach the database in for the entire duration of a function,
// so it cannot become "unattached" while this function is running.
let db = DATABASE.get();
Some(op(unsafe { db.ptr?.as_ref() }))
}

/// Attach database and returns a guard that will un-attach the database when dropped.
/// Has no effect if a database is already attached.
pub fn attach_database<Db: ?Sized + Database, R>(db: &Db, op: impl FnOnce() -> R) -> R {
let _guard = AttachedDb::new(db);
op()
}

#[derive(Copy, Clone, PartialEq, Eq)]
struct AttachedDatabase {
ptr: Option<NonNull<dyn Database>>,
}

impl AttachedDatabase {
pub const fn null() -> Self {
Self { ptr: None }
}

pub fn from<Db: ?Sized + Database>(db: &Db) -> Self {
unsafe {
let db: *const dyn Database = db.as_salsa_database();
Self {
ptr: Some(NonNull::new_unchecked(db as *mut dyn Database)),
}
}
}
}

unsafe impl Send for AttachedDatabase where dyn Database: Sync {}

unsafe impl Sync for AttachedDatabase where dyn Database: Sync {}

struct AttachedDb<'db, Db: ?Sized + Database> {
db: &'db Db,
previous: AttachedDatabase,
}

impl<'db, Db: ?Sized + Database> AttachedDb<'db, Db> {
pub fn new(db: &'db Db) -> Self {
let previous = DATABASE.replace(AttachedDatabase::from(db));
AttachedDb { db, previous }
}
}

impl<Db: ?Sized + Database> Drop for AttachedDb<'_, Db> {
fn drop(&mut self) {
DATABASE.set(self.previous);
}
}

impl<Db: ?Sized + Database> std::ops::Deref for AttachedDb<'_, Db> {
type Target = Db;

fn deref(&self) -> &Db {
&self.db
}
}
Loading

0 comments on commit c84bd1b

Please sign in to comment.