mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-08-04 11:00:05 +00:00
move to QueryType.in_db(&db)
instead of db.query(QueryType)
This will be more compatible once we move to having queries have an associated `DynDb` type. It also reads nicely.
This commit is contained in:
parent
7a5b515279
commit
0e5366df5d
12 changed files with 91 additions and 99 deletions
|
@ -350,6 +350,56 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
|
|||
#[derive(Default, Debug)]
|
||||
#trait_vis struct #qt;
|
||||
|
||||
impl #qt {
|
||||
/// Get access to extra methods pertaining to this query. For
|
||||
/// example, you can use this to run the GC (`sweep`) across a
|
||||
/// single input. You can also use it to invoke this query, though
|
||||
/// it's more common to use the trait method on the database
|
||||
/// itself.
|
||||
#trait_vis fn in_db<DB>(self, db: &DB) -> salsa::QueryTable<'_, DB, Self>
|
||||
where
|
||||
Self: salsa::Query<DB>,
|
||||
DB: salsa::plumbing::GetQueryTable<Self>,
|
||||
{
|
||||
<DB as salsa::plumbing::GetQueryTable<Self>>::get_query_table(db)
|
||||
}
|
||||
|
||||
/// Like `in_db`, but gives access to methods for setting the
|
||||
/// value of an input. Not applicable to derived queries.
|
||||
///
|
||||
/// # Threads, cancellation, and blocking
|
||||
///
|
||||
/// Mutating the value of a query cannot be done while there are
|
||||
/// still other queries executing. If you are using your database
|
||||
/// within a single thread, this is not a problem: you only have
|
||||
/// `&self` access to the database, but this method requires `&mut
|
||||
/// self`.
|
||||
///
|
||||
/// However, if you have used `snapshot` to create other threads,
|
||||
/// then attempts to `set` will **block the current thread** until
|
||||
/// those snapshots are dropped (usually when those threads
|
||||
/// complete). This also implies that if you create a snapshot but
|
||||
/// do not send it to another thread, then invoking `set` will
|
||||
/// deadlock.
|
||||
///
|
||||
/// Before blocking, the thread that is attempting to `set` will
|
||||
/// also set a cancellation flag. In the threads operating on
|
||||
/// snapshots, you can use the [`is_current_revision_canceled`]
|
||||
/// method to check for this flag and bring those operations to a
|
||||
/// close, thus allowing the `set` to succeed. Ignoring this flag
|
||||
/// may lead to "starvation", meaning that the thread attempting
|
||||
/// to `set` has to wait a long, long time. =)
|
||||
///
|
||||
/// [`is_current_revision_canceled`]: struct.Runtime.html#method.is_current_revision_canceled
|
||||
#trait_vis fn in_db_mut<DB>(self, db: &mut DB) -> salsa::QueryTableMut<'_, DB, Self>
|
||||
where
|
||||
Self: salsa::Query<DB>,
|
||||
DB: salsa::plumbing::GetQueryTable<Self>,
|
||||
{
|
||||
<DB as salsa::plumbing::GetQueryTable<Self>>::get_query_table_mut(db)
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe proof obligation: that our key/value are a part
|
||||
// of the `GroupData`.
|
||||
impl<#db> salsa::Query<#db> for #qt
|
||||
|
|
55
src/lib.rs
55
src/lib.rs
|
@ -86,61 +86,6 @@ pub trait Database: plumbing::DatabaseOps {
|
|||
}
|
||||
}
|
||||
|
||||
/// Extension trait that gives access to the `query` and `query_mut` methods.
|
||||
pub trait DatabaseQueryExt: Database {
|
||||
/// Get access to extra methods pertaining to a given query. For
|
||||
/// example, you can use this to run the GC (`sweep`) across a
|
||||
/// single input. You can also use it to invoke a query, though
|
||||
/// it's more common to use the trait method on the database
|
||||
/// itself.
|
||||
#[allow(unused_variables)]
|
||||
fn query<Q>(&self, query: Q) -> QueryTable<'_, Self, Q>
|
||||
where
|
||||
Q: Query<Self>,
|
||||
Self: plumbing::GetQueryTable<Q>,
|
||||
{
|
||||
<Self as plumbing::GetQueryTable<Q>>::get_query_table(self)
|
||||
}
|
||||
|
||||
/// Like `query`, but gives access to methods for setting the
|
||||
/// value of an input.
|
||||
///
|
||||
/// # Threads, cancellation, and blocking
|
||||
///
|
||||
/// Mutating the value of a query cannot be done while there are
|
||||
/// still other queries executing. If you are using your database
|
||||
/// within a single thread, this is not a problem: you only have
|
||||
/// `&self` access to the database, but this method requires `&mut
|
||||
/// self`.
|
||||
///
|
||||
/// However, if you have used `snapshot` to create other threads,
|
||||
/// then attempts to `set` will **block the current thread** until
|
||||
/// those snapshots are dropped (usually when those threads
|
||||
/// complete). This also implies that if you create a snapshot but
|
||||
/// do not send it to another thread, then invoking `set` will
|
||||
/// deadlock.
|
||||
///
|
||||
/// Before blocking, the thread that is attempting to `set` will
|
||||
/// also set a cancellation flag. In the threads operating on
|
||||
/// snapshots, you can use the [`is_current_revision_canceled`]
|
||||
/// method to check for this flag and bring those operations to a
|
||||
/// close, thus allowing the `set` to succeed. Ignoring this flag
|
||||
/// may lead to "starvation", meaning that the thread attempting
|
||||
/// to `set` has to wait a long, long time. =)
|
||||
///
|
||||
/// [`is_current_revision_canceled`]: struct.Runtime.html#method.is_current_revision_canceled
|
||||
#[allow(unused_variables)]
|
||||
fn query_mut<Q>(&mut self, query: Q) -> QueryTableMut<'_, Self, Q>
|
||||
where
|
||||
Q: Query<Self>,
|
||||
Self: plumbing::GetQueryTable<Q>,
|
||||
{
|
||||
<Self as plumbing::GetQueryTable<Q>>::get_query_table_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Database> DatabaseQueryExt for DB {}
|
||||
|
||||
/// The `Event` struct identifies various notable things that can
|
||||
/// occur during salsa execution. Instances of this struct are given
|
||||
/// to `salsa_event`.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::db;
|
||||
use crate::group::*;
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{Database, DatabaseQueryExt, Durability, SweepStrategy};
|
||||
use salsa::{Database, Durability, SweepStrategy};
|
||||
|
||||
#[test]
|
||||
fn compute_one_write_low() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::db;
|
||||
use crate::group::{FibonacciQuery, GcDatabase};
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{Database, DatabaseQueryExt, Durability, SweepStrategy};
|
||||
use salsa::{Database, Durability, SweepStrategy};
|
||||
|
||||
#[test]
|
||||
fn sweep_default() {
|
||||
|
@ -9,7 +9,7 @@ fn sweep_default() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||
let k: Vec<_> = FibonacciQuery.in_db(&db).entries();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime_mut().synthetic_write(Durability::LOW);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::db;
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{Database, DatabaseQueryExt, Durability, InternId, SweepStrategy};
|
||||
use salsa::{Database, Durability, InternId, SweepStrategy};
|
||||
|
||||
/// Query group for tests for how interned keys interact with GC.
|
||||
#[salsa::query_group(Intern)]
|
||||
|
@ -44,7 +44,7 @@ fn discard_during_same_revision() {
|
|||
|
||||
// If we are not careful, this would remove the interned key for
|
||||
// "foo".
|
||||
db.query(InternStrQuery).sweep(
|
||||
InternStrQuery.in_db(&db).sweep(
|
||||
SweepStrategy::default()
|
||||
.discard_everything()
|
||||
.sweep_all_revisions(),
|
||||
|
@ -123,7 +123,7 @@ fn discard_durability_after_synthetic_write_low() {
|
|||
let foo1a = db.repeat_intern1("foo");
|
||||
assert_eq!(
|
||||
Durability::HIGH,
|
||||
db.query(RepeatIntern1Query).durability("foo")
|
||||
RepeatIntern1Query.in_db(&db).durability("foo")
|
||||
);
|
||||
|
||||
// Trigger a new revision.
|
||||
|
@ -131,7 +131,7 @@ fn discard_durability_after_synthetic_write_low() {
|
|||
|
||||
// If we are not careful, this would remove the interned key for
|
||||
// "foo".
|
||||
db.query(InternStrQuery).sweep(
|
||||
InternStrQuery.in_db(&db).sweep(
|
||||
SweepStrategy::default()
|
||||
.discard_everything()
|
||||
.sweep_all_revisions(),
|
||||
|
@ -163,14 +163,14 @@ fn discard_durability_after_synthetic_write_high() {
|
|||
let foo1a = db.repeat_intern1("foo");
|
||||
assert_eq!(
|
||||
Durability::HIGH,
|
||||
db.query(RepeatIntern1Query).durability("foo")
|
||||
RepeatIntern1Query.in_db(&db).durability("foo")
|
||||
);
|
||||
|
||||
// Trigger a new revision -- marking even high things as having changed.
|
||||
db.salsa_runtime_mut().synthetic_write(Durability::HIGH);
|
||||
|
||||
// We are now able to collect "collect".
|
||||
db.query(InternStrQuery).sweep(
|
||||
InternStrQuery.in_db(&db).sweep(
|
||||
SweepStrategy::default()
|
||||
.discard_everything()
|
||||
.sweep_all_revisions(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
macro_rules! assert_keys {
|
||||
($db:expr, $($query:expr => ($($key:expr),*),)*) => {
|
||||
$(
|
||||
let entries = $db.query($query).entries::<Vec<_>>();
|
||||
let entries = $query.in_db(&$db).entries::<Vec<_>>();
|
||||
let mut keys = entries.into_iter().map(|e| e.key).collect::<Vec<_>>();
|
||||
keys.sort();
|
||||
assert_eq!(keys, vec![$($key),*], "query {:?} had wrong keys", $query);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::db;
|
||||
use crate::group::{FibonacciQuery, GcDatabase};
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{Database, DatabaseQueryExt, Durability, SweepStrategy};
|
||||
use salsa::{Database, Durability, SweepStrategy};
|
||||
|
||||
// For constant values (like `fibonacci`), we only keep the values
|
||||
// that were used in the latest revision, not the sub-values that
|
||||
|
@ -13,7 +13,7 @@ fn one_rev() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||
let k: Vec<_> = FibonacciQuery.in_db(&db).entries();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
// Everything was used in this revision, so
|
||||
|
@ -28,7 +28,7 @@ fn two_rev_nothing() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||
let k: Vec<_> = FibonacciQuery.in_db(&db).entries();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime_mut().synthetic_write(Durability::LOW);
|
||||
|
@ -37,7 +37,7 @@ fn two_rev_nothing() {
|
|||
// everything gets collected.
|
||||
db.sweep_all(SweepStrategy::discard_outdated());
|
||||
|
||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||
let k: Vec<_> = FibonacciQuery.in_db(&db).entries();
|
||||
assert_eq!(k.len(), 0);
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ fn two_rev_one_use() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||
let k: Vec<_> = FibonacciQuery.in_db(&db).entries();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime_mut().synthetic_write(Durability::LOW);
|
||||
|
@ -70,7 +70,7 @@ fn two_rev_two_uses() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||
let k: Vec<_> = FibonacciQuery.in_db(&db).entries();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime_mut().synthetic_write(Durability::LOW);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::db;
|
||||
use salsa::{Database, DatabaseQueryExt, SweepStrategy};
|
||||
use salsa::{Database, SweepStrategy};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -60,7 +60,7 @@ fn consistency_with_gc() {
|
|||
let v1 = db.repeat1();
|
||||
|
||||
cell.store(23, Ordering::SeqCst);
|
||||
db.query(VolatileQuery).sweep(
|
||||
VolatileQuery.in_db(&db).sweep(
|
||||
SweepStrategy::default()
|
||||
.discard_everything()
|
||||
.sweep_all_revisions(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::implementation::{TestContext, TestContextImpl};
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{DatabaseQueryExt, Durability};
|
||||
use salsa::Durability;
|
||||
|
||||
#[salsa::query_group(Constants)]
|
||||
pub(crate) trait ConstantsDatabase: TestContext {
|
||||
|
@ -64,56 +64,56 @@ fn set_after_constant_same_value() {
|
|||
|
||||
#[test]
|
||||
fn not_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
let mut db = TestContextImpl::default();
|
||||
|
||||
db.set_input('a', 22);
|
||||
db.set_input('b', 44);
|
||||
assert_eq!(db.add('a', 'b'), 66);
|
||||
assert_eq!(Durability::LOW, db.query(AddQuery).durability(('a', 'b')));
|
||||
assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn durability() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
let mut db = TestContextImpl::default();
|
||||
|
||||
db.set_input_with_durability('a', 22, Durability::HIGH);
|
||||
db.set_input_with_durability('b', 44, Durability::HIGH);
|
||||
assert_eq!(db.add('a', 'b'), 66);
|
||||
assert_eq!(Durability::HIGH, db.query(AddQuery).durability(('a', 'b')));
|
||||
assert_eq!(Durability::HIGH, AddQuery.in_db(&db).durability(('a', 'b')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
let mut db = TestContextImpl::default();
|
||||
|
||||
db.set_input_with_durability('a', 22, Durability::HIGH);
|
||||
db.set_input('b', 44);
|
||||
assert_eq!(db.add('a', 'b'), 66);
|
||||
assert_eq!(Durability::LOW, db.query(AddQuery).durability(('a', 'b')));
|
||||
assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn becomes_constant_with_change() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
let mut db = TestContextImpl::default();
|
||||
|
||||
db.set_input('a', 22);
|
||||
db.set_input('b', 44);
|
||||
assert_eq!(db.add('a', 'b'), 66);
|
||||
assert_eq!(Durability::LOW, db.query(AddQuery).durability(('a', 'b')));
|
||||
assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b')));
|
||||
|
||||
db.set_input_with_durability('a', 23, Durability::HIGH);
|
||||
assert_eq!(db.add('a', 'b'), 67);
|
||||
assert_eq!(Durability::LOW, db.query(AddQuery).durability(('a', 'b')));
|
||||
assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b')));
|
||||
|
||||
db.set_input_with_durability('b', 45, Durability::HIGH);
|
||||
assert_eq!(db.add('a', 'b'), 68);
|
||||
assert_eq!(Durability::HIGH, db.query(AddQuery).durability(('a', 'b')));
|
||||
assert_eq!(Durability::HIGH, AddQuery.in_db(&db).durability(('a', 'b')));
|
||||
|
||||
db.set_input_with_durability('b', 45, Durability::MEDIUM);
|
||||
assert_eq!(db.add('a', 'b'), 68);
|
||||
assert_eq!(
|
||||
Durability::MEDIUM,
|
||||
db.query(AddQuery).durability(('a', 'b'))
|
||||
AddQuery.in_db(&db).durability(('a', 'b'))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ fn becomes_constant_with_change() {
|
|||
// being constant.
|
||||
#[test]
|
||||
fn constant_to_non_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
let mut db = TestContextImpl::default();
|
||||
|
||||
db.set_input_with_durability('a', 11, Durability::HIGH);
|
||||
db.set_input_with_durability('b', 22, Durability::HIGH);
|
||||
|
|
12
tests/lru.rs
12
tests/lru.rs
|
@ -4,8 +4,6 @@ use std::sync::{
|
|||
Arc,
|
||||
};
|
||||
|
||||
use salsa::DatabaseQueryExt as _;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct HotPotato(u32);
|
||||
|
||||
|
@ -51,7 +49,7 @@ impl salsa::Database for Database {}
|
|||
#[test]
|
||||
fn lru_works() {
|
||||
let mut db = Database::default();
|
||||
db.query_mut(GetQuery).set_lru_capacity(32);
|
||||
GetQuery.in_db_mut(&mut db).set_lru_capacity(32);
|
||||
assert_eq!(N_POTATOES.load(Ordering::SeqCst), 0);
|
||||
|
||||
for i in 0..128u32 {
|
||||
|
@ -66,10 +64,10 @@ fn lru_works() {
|
|||
}
|
||||
assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32);
|
||||
|
||||
db.query_mut(GetQuery).set_lru_capacity(32);
|
||||
GetQuery.in_db_mut(&mut db).set_lru_capacity(32);
|
||||
assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32);
|
||||
|
||||
db.query_mut(GetQuery).set_lru_capacity(64);
|
||||
GetQuery.in_db_mut(&mut db).set_lru_capacity(64);
|
||||
assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32);
|
||||
for i in 0..128u32 {
|
||||
let p = db.get(i);
|
||||
|
@ -78,7 +76,7 @@ fn lru_works() {
|
|||
assert_eq!(N_POTATOES.load(Ordering::SeqCst), 64);
|
||||
|
||||
// Special case: setting capacity to zero disables LRU
|
||||
db.query_mut(GetQuery).set_lru_capacity(0);
|
||||
GetQuery.in_db_mut(&mut db).set_lru_capacity(0);
|
||||
assert_eq!(N_POTATOES.load(Ordering::SeqCst), 64);
|
||||
for i in 0..128u32 {
|
||||
let p = db.get(i);
|
||||
|
@ -93,7 +91,7 @@ fn lru_works() {
|
|||
#[test]
|
||||
fn lru_doesnt_break_volatile_queries() {
|
||||
let mut db = Database::default();
|
||||
db.query_mut(GetVolatileQuery).set_lru_capacity(32);
|
||||
GetVolatileQuery.in_db_mut(&mut db).set_lru_capacity(32);
|
||||
// Here, we check that we execute each volatile query at most once, despite
|
||||
// LRU. That does mean that we have more values in DB than the LRU capacity,
|
||||
// but it's much better than inconsistent results from volatile queries!
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
use std::{cell::Cell, collections::HashMap, rc::Rc};
|
||||
|
||||
use salsa::{Database as _, DatabaseQueryExt as _, Durability};
|
||||
use salsa::{Database as _, Durability};
|
||||
|
||||
#[salsa::query_group(QueryGroupStorage)]
|
||||
trait QueryGroup: salsa::Database + AsRef<HashMap<u32, u32>> {
|
||||
|
@ -70,7 +70,7 @@ fn on_demand_input_works() {
|
|||
assert_eq!(db.b(1), 10);
|
||||
assert_eq!(db.a(1), 10);
|
||||
|
||||
db.query_mut(AQuery).invalidate(&1);
|
||||
AQuery.in_db_mut(&mut db).invalidate(&1);
|
||||
assert_eq!(db.b(1), 92);
|
||||
assert_eq!(db.a(1), 92);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use rand::seq::SliceRandom;
|
|||
use rand::Rng;
|
||||
|
||||
use salsa::Database;
|
||||
use salsa::DatabaseQueryExt;
|
||||
use salsa::ParallelDatabase;
|
||||
use salsa::Snapshot;
|
||||
use salsa::SweepStrategy;
|
||||
|
@ -162,13 +161,13 @@ impl ReadOp {
|
|||
},
|
||||
ReadOp::Gc(query, strategy) => match query {
|
||||
Query::A => {
|
||||
db.query(AQuery).sweep(strategy);
|
||||
AQuery.in_db(db).sweep(strategy);
|
||||
}
|
||||
Query::B => {
|
||||
db.query(BQuery).sweep(strategy);
|
||||
BQuery.in_db(db).sweep(strategy);
|
||||
}
|
||||
Query::C => {
|
||||
db.query(CQuery).sweep(strategy);
|
||||
CQuery.in_db(db).sweep(strategy);
|
||||
}
|
||||
},
|
||||
ReadOp::GcAll(strategy) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue