Skip to content

Commit bcca0a3

Browse files
committed
Move memo dropping off to another thread
1 parent 0414d89 commit bcca0a3

File tree

22 files changed

+423
-214
lines changed

22 files changed

+423
-214
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ salsa-macros = { version = "0.22.0", path = "components/salsa-macros", optional
1414

1515
boxcar = { version = "0.2.12" }
1616
crossbeam-queue = "0.3.11"
17+
crossbeam-utils = "0.8.21"
1718
dashmap = { version = "6", features = ["raw-api"] }
1819
# the version of hashbrown used by dashmap
1920
hashbrown_14 = { version = "0.14", package = "hashbrown" }

components/salsa-macro-rules/src/setup_tracked_fn.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ macro_rules! setup_tracked_fn {
243243
zalsa: &$zalsa::Zalsa,
244244
first_index: $zalsa::IngredientIndex,
245245
struct_index: $zalsa::IngredientIndices,
246+
memo_drop_sender: $zalsa::MemoDropSender,
246247
) -> Vec<Box<dyn $zalsa::Ingredient>> {
247248
let struct_index: $zalsa::IngredientIndices = $zalsa::macro_if! {
248249
if $needs_interner {
@@ -276,12 +277,16 @@ macro_rules! setup_tracked_fn {
276277
)
277278
};
278279

279-
let fn_ingredient = <$zalsa::function::IngredientImpl<$Configuration>>::new(
280-
first_index,
281-
memo_ingredient_indices,
282-
$lru,
283-
zalsa.views().downcaster_for::<dyn $Db>(),
284-
);
280+
// SAFETY: We pass the MemoEntryType for this Configuration, and we lookup the memo types table correctly.
281+
let fn_ingredient = unsafe {
282+
<$zalsa::function::IngredientImpl<$Configuration>>::new(
283+
first_index,
284+
memo_ingredient_indices,
285+
$lru,
286+
zalsa.views().downcaster_for::<dyn $Db>(),
287+
memo_drop_sender
288+
)
289+
};
285290
$zalsa::macro_if! {
286291
if $needs_interner {
287292
vec![

examples/lazy-input/main.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,18 @@ impl LazyInputDatabase {
8989
fn new(tx: Sender<DebounceEventResult>) -> Self {
9090
let logs: Arc<Mutex<Vec<String>>> = Default::default();
9191
Self {
92-
storage: Storage::new(Some(Box::new({
93-
let logs = logs.clone();
94-
move |event| {
95-
// don't log boring events
96-
if let salsa::EventKind::WillExecute { .. } = event.kind {
97-
logs.lock().unwrap().push(format!("{event:?}"));
92+
storage: Storage::new(
93+
false,
94+
Some(Box::new({
95+
let logs = logs.clone();
96+
move |event| {
97+
// don't log boring events
98+
if let salsa::EventKind::WillExecute { .. } = event.kind {
99+
logs.lock().unwrap().push(format!("{event:?}"));
100+
}
98101
}
99-
}
100-
}))),
102+
})),
103+
),
101104
logs,
102105
files: DashMap::new(),
103106
file_watcher: Arc::new(Mutex::new(

src/accumulator.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use accumulated::{Accumulated, AnyAccumulated};
1010
use crate::cycle::CycleHeads;
1111
use crate::function::VerifyResult;
1212
use crate::ingredient::{Ingredient, Jar};
13-
use crate::plumbing::{IngredientIndices, ZalsaLocal};
13+
use crate::plumbing::{IngredientIndices, MemoDropSender, ZalsaLocal};
1414
use crate::sync::Arc;
1515
use crate::table::memo::MemoTableTypes;
1616
use crate::zalsa::{IngredientIndex, Zalsa};
@@ -47,6 +47,7 @@ impl<A: Accumulator> Jar for JarImpl<A> {
4747
_zalsa: &Zalsa,
4848
first_index: IngredientIndex,
4949
_dependencies: IngredientIndices,
50+
_: MemoDropSender,
5051
) -> Vec<Box<dyn Ingredient>> {
5152
vec![Box::new(<IngredientImpl<A>>::new(first_index))]
5253
}

src/database_impl.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ impl Default for DatabaseImpl {
1414
fn default() -> Self {
1515
Self {
1616
// Default behavior: tracing debug log the event.
17-
storage: Storage::new(if tracing::enabled!(Level::DEBUG) {
18-
Some(Box::new(|event| {
19-
tracing::debug!("salsa_event({:?})", event)
20-
}))
21-
} else {
22-
None
23-
}),
17+
storage: Storage::new(
18+
false,
19+
if tracing::enabled!(Level::DEBUG) {
20+
Some(Box::new(|event| {
21+
tracing::debug!("salsa_event({:?})", event)
22+
}))
23+
} else {
24+
None
25+
},
26+
),
2427
}
2528
}
2629
}

src/exclusive.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Bare-bones polyfill for the unstable [`std::sync::Exclusive`] type.
2+
3+
pub struct Exclusive<T: ?Sized> {
4+
inner: T,
5+
}
6+
7+
// SAFETY: We only hand out mutable access to the inner value through a mutable reference to the
8+
// wrapper.
9+
// Therefore we cannot alias the inner value making it trivially sync.
10+
unsafe impl<T> Sync for Exclusive<T> {}
11+
12+
impl<T> Exclusive<T> {
13+
pub fn new(inner: T) -> Self {
14+
Self { inner }
15+
}
16+
17+
pub fn get_mut(&mut self) -> &mut T {
18+
&mut self.inner
19+
}
20+
}

src/function.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use std::any::Any;
22
use std::fmt;
3+
use std::marker::PhantomData;
34
use std::ptr::NonNull;
45

56
pub(crate) use maybe_changed_after::VerifyResult;
67

78
use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues};
89
use crate::cycle::{CycleHeadKind, CycleHeads, CycleRecoveryAction, CycleRecoveryStrategy};
9-
use crate::function::delete::DeletedEntries;
1010
use crate::function::sync::{ClaimResult, SyncTable};
1111
use crate::ingredient::Ingredient;
1212
use crate::key::DatabaseKeyIndex;
13-
use crate::plumbing::MemoIngredientMap;
13+
use crate::plumbing::{MemoDropSender, MemoIngredientMap};
1414
use crate::salsa_struct::SalsaStructInDb;
1515
use crate::sync::Arc;
1616
use crate::table::memo::MemoTableTypes;
@@ -22,7 +22,6 @@ use crate::{Database, Id, Revision};
2222

2323
mod accumulated;
2424
mod backdate;
25-
mod delete;
2625
mod diff_outputs;
2726
mod execute;
2827
mod fetch;
@@ -137,7 +136,8 @@ pub struct IngredientImpl<C: Configuration> {
137136
/// current revision: you would be right, but we are being defensive, because
138137
/// we don't know that we can trust the database to give us the same runtime
139138
/// everytime and so forth.
140-
deleted_entries: DeletedEntries<C>,
139+
delete: MemoDropSender,
140+
config: PhantomData<fn(C) -> C>,
141141
}
142142

143143
impl<C> IngredientImpl<C>
@@ -149,14 +149,16 @@ where
149149
memo_ingredient_indices: <C::SalsaStruct<'static> as SalsaStructInDb>::MemoIngredientMap,
150150
lru: usize,
151151
view_caster: DatabaseDownCaster<C::DbView>,
152+
delete: MemoDropSender,
152153
) -> Self {
153154
Self {
154155
index,
155156
memo_ingredient_indices,
156157
lru: lru::Lru::new(lru),
157-
deleted_entries: Default::default(),
158158
view_caster,
159159
sync_table: SyncTable::new(index),
160+
delete,
161+
config: PhantomData,
160162
}
161163
}
162164

@@ -196,16 +198,7 @@ where
196198
// FIXME: Use `Box::into_non_null` once stable
197199
let memo = NonNull::from(Box::leak(Box::new(memo)));
198200

199-
if let Some(old_value) =
200-
self.insert_memo_into_table_for(zalsa, id, memo, memo_ingredient_index)
201-
{
202-
// In case there is a reference to the old memo out there, we have to store it
203-
// in the deleted entries. This will get cleared when a new revision starts.
204-
//
205-
// SAFETY: Once the revision starts, there will be no outstanding borrows to the
206-
// memo contents, and so it will be safe to free.
207-
unsafe { self.deleted_entries.push(old_value) };
208-
}
201+
self.insert_memo_into_table_for(zalsa, id, memo, memo_ingredient_index);
209202
// SAFETY: memo has been inserted into the table
210203
unsafe { self.extend_memo_lifetime(memo.as_ref()) }
211204
}
@@ -301,10 +294,9 @@ where
301294
Self::evict_value_from_memo_for(
302295
table.memos_mut(evict),
303296
self.memo_ingredient_indices.get(ingredient_index),
297+
&self.delete,
304298
)
305299
});
306-
307-
self.deleted_entries.clear();
308300
}
309301

310302
fn debug_name(&self) -> &'static str {

src/function/delete.rs

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/function/memo.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::ptr::NonNull;
66
use crate::cycle::{empty_cycle_heads, CycleHeadKind, CycleHeads};
77
use crate::function::{Configuration, IngredientImpl};
88
use crate::key::DatabaseKeyIndex;
9+
use crate::plumbing::MemoDropSender;
910
use crate::revision::AtomicRevision;
1011
use crate::sync::atomic::Ordering;
1112
use crate::table::memo::MemoTableWithTypesMut;
@@ -16,27 +17,23 @@ use crate::{Event, EventKind, Id, Revision};
1617
impl<C: Configuration> IngredientImpl<C> {
1718
/// Inserts the memo for the given key; (atomically) overwrites and returns any previously existing memo
1819
pub(super) fn insert_memo_into_table_for<'db>(
19-
&self,
20+
&'db self,
2021
zalsa: &'db Zalsa,
2122
id: Id,
2223
memo: NonNull<Memo<C::Output<'db>>>,
2324
memo_ingredient_index: MemoIngredientIndex,
24-
) -> Option<NonNull<Memo<C::Output<'db>>>> {
25+
) {
2526
// SAFETY: The table stores 'static memos (to support `Any`), the memos are in fact valid
2627
// for `'db` though as we delay their dropping to the end of a revision.
2728
let static_memo = unsafe {
2829
transmute::<NonNull<Memo<C::Output<'db>>>, NonNull<Memo<C::Output<'static>>>>(memo)
2930
};
30-
let old_static_memo = zalsa
31+
let old_memo = zalsa
3132
.memo_table_for(id)
32-
.insert(memo_ingredient_index, static_memo)?;
33-
// SAFETY: The table stores 'static memos (to support `Any`), the memos are in fact valid
34-
// for `'db` though as we delay their dropping to the end of a revision.
35-
Some(unsafe {
36-
transmute::<NonNull<Memo<C::Output<'static>>>, NonNull<Memo<C::Output<'db>>>>(
37-
old_static_memo,
38-
)
39-
})
33+
.insert(memo_ingredient_index, static_memo);
34+
if let Some(old_memo) = old_memo {
35+
self.delete.delay(old_memo);
36+
}
4037
}
4138

4239
/// Loads the current memo for `key_index`. This does not hold any sort of
@@ -62,9 +59,11 @@ impl<C: Configuration> IngredientImpl<C> {
6259
pub(super) fn evict_value_from_memo_for(
6360
table: MemoTableWithTypesMut<'_>,
6461
memo_ingredient_index: MemoIngredientIndex,
62+
delayed: &MemoDropSender,
6563
) {
66-
let map = |memo: &mut Memo<C::Output<'static>>| {
67-
match &memo.revisions.origin {
64+
if let Some(memo) = table.fetch_raw::<Memo<C::Output<'static>>>(memo_ingredient_index) {
65+
// SAFETY: The memo is live
66+
match unsafe { &memo.as_ref().revisions.origin } {
6867
QueryOrigin::Assigned(_)
6968
| QueryOrigin::DerivedUntracked(_)
7069
| QueryOrigin::FixpointInitial => {
@@ -73,14 +72,9 @@ impl<C: Configuration> IngredientImpl<C> {
7372
// or those with untracked inputs
7473
// as their values cannot be reconstructed.
7574
}
76-
QueryOrigin::Derived(_) => {
77-
// Set the memo value to `None`.
78-
memo.value = None;
79-
}
75+
QueryOrigin::Derived(_) => delayed.clear_value(memo),
8076
}
81-
};
82-
83-
table.map_memo(memo_ingredient_index, map)
77+
}
8478
}
8579
}
8680

@@ -255,4 +249,7 @@ impl<V: Send + Sync + Any> crate::table::memo::Memo for Memo<V> {
255249
fn origin(&self) -> &QueryOrigin {
256250
&self.revisions.origin
257251
}
252+
fn clear_value(&mut self) {
253+
self.value = None;
254+
}
258255
}

src/ingredient.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt;
44
use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues};
55
use crate::cycle::{CycleHeadKind, CycleHeads, CycleRecoveryStrategy};
66
use crate::function::VerifyResult;
7-
use crate::plumbing::IngredientIndices;
7+
use crate::plumbing::{IngredientIndices, MemoDropSender};
88
use crate::sync::Arc;
99
use crate::table::memo::MemoTableTypes;
1010
use crate::table::Table;
@@ -33,6 +33,7 @@ pub trait Jar: Any {
3333
zalsa: &Zalsa,
3434
first_index: IngredientIndex,
3535
dependencies: IngredientIndices,
36+
memo_drop_sender: MemoDropSender,
3637
) -> Vec<Box<dyn Ingredient>>
3738
where
3839
Self: Sized;

0 commit comments

Comments
 (0)