-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Each clone gets an independent local state.
- Loading branch information
1 parent
f3480bd
commit 22a8157
Showing
17 changed files
with
449 additions
and
466 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use std::{cell::Cell, ptr::NonNull}; | ||
|
||
use crate::Database; | ||
|
||
thread_local! { | ||
/// The thread-local state salsa requires for a given thread | ||
static ATTACHED: Attached = const { Attached::new() } | ||
} | ||
|
||
/// State that is specific to a single execution thread. | ||
/// | ||
/// Internally, this type uses ref-cells. | ||
/// | ||
/// **Note also that all mutations to the database handle (and hence | ||
/// to the local-state) must be undone during unwinding.** | ||
struct Attached { | ||
/// Pointer to the currently attached database. | ||
database: Cell<Option<NonNull<dyn Database>>>, | ||
} | ||
|
||
impl Attached { | ||
const fn new() -> Self { | ||
Self { | ||
database: Cell::new(None), | ||
} | ||
} | ||
|
||
fn attach<Db, R>(&self, db: &Db, op: impl FnOnce(&Db) -> R) -> R | ||
where | ||
Db: ?Sized + Database, | ||
{ | ||
struct DbGuard<'s> { | ||
state: Option<&'s Attached>, | ||
} | ||
|
||
impl<'s> DbGuard<'s> { | ||
fn new(attached: &'s Attached, db: &dyn Database) -> Self { | ||
if let Some(current_db) = attached.database.get() { | ||
// Already attached? Assert that the database has not changed. | ||
assert_eq!( | ||
current_db, | ||
NonNull::from(db), | ||
"cannot change database mid-query", | ||
); | ||
Self { state: None } | ||
} else { | ||
// Otherwise, set the database. | ||
attached.database.set(Some(NonNull::from(db))); | ||
Self { | ||
state: Some(attached), | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Drop for DbGuard<'_> { | ||
fn drop(&mut self) { | ||
// Reset database to null if we did anything in `DbGuard::new`. | ||
if let Some(attached) = self.state { | ||
attached.database.set(None); | ||
} | ||
} | ||
} | ||
|
||
let _guard = DbGuard::new(self, db.as_dyn_database()); | ||
op(db) | ||
} | ||
|
||
/// Access the "attached" database. Returns `None` if no database is attached. | ||
/// Databases are attached with `attach_database`. | ||
fn with<R>(&self, op: impl FnOnce(&dyn Database) -> R) -> Option<R> { | ||
if let Some(db) = self.database.get() { | ||
// SAFETY: We always attach the database in for the entire duration of a function, | ||
// so it cannot become "unattached" while this function is running. | ||
Some(op(unsafe { db.as_ref() })) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
/// Attach the database to the current thread and execute `op`. | ||
/// Panics if a different database has already been attached. | ||
pub(crate) fn attach<R, Db>(db: &Db, op: impl FnOnce(&Db) -> R) -> R | ||
where | ||
Db: ?Sized + Database, | ||
{ | ||
ATTACHED.with(|a| a.attach(db, op)) | ||
} | ||
|
||
/// 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> { | ||
ATTACHED.with(|a| a.with(op)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.