diff --git a/components/salsa-macro-rules/src/setup_input_struct.rs b/components/salsa-macro-rules/src/setup_input_struct.rs index 51cd482bc..112e46330 100644 --- a/components/salsa-macro-rules/src/setup_input_struct.rs +++ b/components/salsa-macro-rules/src/setup_input_struct.rs @@ -72,7 +72,7 @@ macro_rules! setup_input_struct { impl $zalsa_struct::Configuration for $Configuration { const DEBUG_NAME: &'static str = stringify!($Struct); const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*]; - const IS_SINGLETON: bool = $is_singleton; + type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NoSingleton}}; /// The input struct (which wraps an `Id`) type Struct = $Struct; diff --git a/src/input.rs b/src/input.rs index ee08e959b..3ef7a2eb3 100644 --- a/src/input.rs +++ b/src/input.rs @@ -2,16 +2,16 @@ use std::{any::Any, fmt, ops::DerefMut}; pub mod input_field; pub mod setter; +pub mod singleton; -use crossbeam::atomic::AtomicCell; use input_field::FieldIngredientImpl; -use parking_lot::Mutex; use crate::{ accumulator::accumulated_map::InputAccumulatedValues, cycle::CycleRecoveryStrategy, id::{AsId, FromId}, ingredient::{fmt_index, Ingredient}, + input::singleton::{Singleton, SingletonChoice}, key::{DatabaseKeyIndex, DependencyIndex}, plumbing::{Jar, JarAux, Stamp}, table::{memo::MemoTable, sync::SyncTable, Slot, Table}, @@ -23,7 +23,9 @@ use crate::{ pub trait Configuration: Any { const DEBUG_NAME: &'static str; const FIELD_DEBUG_NAMES: &'static [&'static str]; - const IS_SINGLETON: bool; + + /// The singleton state for this input if any. + type Singleton: SingletonChoice + Send + Sync; /// The input struct (which wraps an `Id`) type Struct: FromId + 'static + Send + Sync; @@ -65,8 +67,7 @@ impl Jar for JarImpl { pub struct IngredientImpl { ingredient_index: IngredientIndex, - singleton_index: AtomicCell>, - singleton_lock: Mutex<()>, + singleton: C::Singleton, _phantom: std::marker::PhantomData, } @@ -74,8 +75,7 @@ impl IngredientImpl { pub fn new(index: IngredientIndex) -> Self { Self { ingredient_index: index, - singleton_index: AtomicCell::new(None), - singleton_lock: Default::default(), + singleton: Default::default(), _phantom: std::marker::PhantomData, } } @@ -98,29 +98,15 @@ impl IngredientImpl { pub fn new_input(&self, db: &dyn Database, fields: C::Fields, stamps: C::Stamps) -> C::Struct { let (zalsa, zalsa_local) = db.zalsas(); - // If declared as a singleton, only allow a single instance - let guard = if C::IS_SINGLETON { - let guard = self.singleton_lock.lock(); - if self.singleton_index.load().is_some() { - panic!("singleton struct may not be duplicated"); - } - Some(guard) - } else { - None - }; - - let id = zalsa_local.allocate(zalsa.table(), self.ingredient_index, || Value:: { - fields, - stamps, - memos: Default::default(), - syncs: Default::default(), + let id = self.singleton.with_lock(|| { + zalsa_local.allocate(zalsa.table(), self.ingredient_index, || Value:: { + fields, + stamps, + memos: Default::default(), + syncs: Default::default(), + }) }); - if C::IS_SINGLETON { - self.singleton_index.store(Some(id)); - drop(guard); - } - FromId::from_id(id) } @@ -159,13 +145,12 @@ impl IngredientImpl { setter(&mut r.fields) } - /// Get the singleton input previously created (if any). - pub fn get_singleton_input(&self) -> Option { - assert!( - C::IS_SINGLETON, - "get_singleton_input invoked on a non-singleton" - ); - self.singleton_index.load().map(FromId::from_id) + /// Get the singleton input previously created. + pub fn get_singleton_input(&self) -> Option + where + C: Configuration, + { + self.singleton.index().map(FromId::from_id) } /// Access field of an input. diff --git a/src/input/singleton.rs b/src/input/singleton.rs new file mode 100644 index 000000000..8917a963f --- /dev/null +++ b/src/input/singleton.rs @@ -0,0 +1,55 @@ +use crossbeam::atomic::AtomicCell; +use parking_lot::Mutex; + +use crate::{id::FromId, Id}; + +mod sealed { + pub trait Sealed {} +} + +pub trait SingletonChoice: sealed::Sealed + Default { + fn with_lock(&self, cb: impl FnOnce() -> Id) -> Id; + fn index(&self) -> Option; +} + +pub struct Singleton { + index: AtomicCell>, + lock: Mutex<()>, +} +impl sealed::Sealed for Singleton {} +impl SingletonChoice for Singleton { + fn with_lock(&self, cb: impl FnOnce() -> Id) -> Id { + let _guard = self.lock.lock(); + if self.index.load().is_some() { + panic!("singleton struct may not be duplicated"); + } + let id = cb(); + self.index.store(Some(id)); + drop(_guard); + id + } + + fn index(&self) -> Option { + self.index.load().map(FromId::from_id) + } +} + +impl Default for Singleton { + fn default() -> Self { + Self { + index: AtomicCell::new(None), + lock: Default::default(), + } + } +} +#[derive(Default)] +pub struct NoSingleton; +impl sealed::Sealed for NoSingleton {} +impl SingletonChoice for NoSingleton { + fn with_lock(&self, cb: impl FnOnce() -> Id) -> Id { + cb() + } + fn index(&self) -> Option { + None + } +} diff --git a/src/lib.rs b/src/lib.rs index 8cc739fab..667d35672 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,8 @@ pub mod plumbing { pub mod input { pub use crate::input::input_field::FieldIngredientImpl; pub use crate::input::setter::SetterImpl; + pub use crate::input::singleton::NoSingleton; + pub use crate::input::singleton::Singleton; pub use crate::input::Configuration; pub use crate::input::HasBuilder; pub use crate::input::IngredientImpl;