use std::sync::Arc; use crate::{ resource::{unwrap_constructed, Resource}, slot::SlotDesc, state::Aerosol, }; /// Implemented for values which can be constructed from other resources. pub trait Constructible: Sized { /// Error type for when resource fails to be constructed. type Error: Into + Send + Sync; /// Construct the resource with the provided application state. fn construct(aero: &Aerosol) -> Result; } /// Automatically implemented for values which can be indirectly constructed from other resources. pub trait IndirectlyConstructible: Sized { /// Error type for when resource fails to be constructed. type Error: Into + Send + Sync; /// Construct the resource with the provided application state. fn construct(aero: &Aerosol) -> Result; } impl IndirectlyConstructible for T { type Error = T::Error; fn construct(aero: &Aerosol) -> Result { T::construct(aero) } } macro_rules! impl_constructible { (<$t:ident>; $($x:ty: $y:expr;)*) => { $( impl<$t: IndirectlyConstructible> IndirectlyConstructible for $x { type Error = $t::Error; fn construct(aero: &Aerosol) -> Result { $t::construct(aero).map($y) } } )* }; } impl_constructible! { ; Arc: Arc::new; std::sync::Mutex: std::sync::Mutex::new; parking_lot::Mutex: parking_lot::Mutex::new; std::sync::RwLock: std::sync::RwLock::new; parking_lot::RwLock: parking_lot::RwLock::new; } /// Implemented for resources which can be constructed from other resources. pub trait ConstructibleResource: Resource + IndirectlyConstructible {} impl ConstructibleResource for T {} impl Aerosol { /// Try to get or construct an instance of `T`. pub fn try_obtain(&self) -> Result { match self.try_get_slot() { Some(SlotDesc::Filled(x)) => Ok(x), Some(SlotDesc::Placeholder) | None => match self.wait_for_slot::(true) { Some(x) => Ok(x), None => match T::construct(self) { Ok(x) => { self.fill_placeholder::(x.clone()); Ok(x) } Err(e) => { self.clear_placeholder::(); Err(e) } }, }, } } /// Get or construct an instance of `T`. Panics if unable. pub fn obtain(&self) -> T { unwrap_constructed::(self.try_obtain::()) } /// Try to initialize an instance of `T`. Does nothing if `T` is already initialized. pub fn try_init(&self) -> Result<(), T::Error> { match self.wait_for_slot::(true) { Some(_) => Ok(()), None => match T::construct(self) { Ok(x) => { self.fill_placeholder::(x); Ok(()) } Err(e) => { self.clear_placeholder::(); Err(e) } }, } } /// Initialize an instance of `T`. Does nothing if `T` is already initialized. Panics if unable. pub fn init(&self) { unwrap_constructed::(self.try_init::()) } } #[cfg(test)] mod tests { use std::{convert::Infallible, thread::scope, time::Duration}; use super::*; #[derive(Debug, Clone)] struct Dummy; impl Constructible for Dummy { type Error = Infallible; fn construct(_app_state: &Aerosol) -> Result { std::thread::sleep(Duration::from_millis(100)); Ok(Self) } } #[test] fn obtain() { let state = Aerosol::new(); state.obtain::(); } #[test] fn obtain_race() { let state = Aerosol::new(); scope(|s| { for _ in 0..100 { s.spawn(|| state.obtain::()); } }); } #[derive(Debug, Clone)] struct DummyRecursive; impl Constructible for DummyRecursive { type Error = Infallible; fn construct(aero: &Aerosol) -> Result { aero.obtain::(); Ok(Self) } } #[test] fn obtain_recursive() { let state = Aerosol::new(); state.obtain::(); } #[test] fn obtain_recursive_race() { let state = Aerosol::new(); scope(|s| { for _ in 0..100 { s.spawn(|| state.obtain::()); } }); } #[derive(Debug, Clone)] struct DummyCyclic; impl Constructible for DummyCyclic { type Error = Infallible; fn construct(aero: &Aerosol) -> Result { aero.obtain::(); Ok(Self) } } #[test] #[should_panic(expected = "Cycle detected")] fn obtain_cyclic() { let state = Aerosol::new(); state.obtain::(); } #[derive(Debug)] struct DummyNonClone; impl Constructible for DummyNonClone { type Error = Infallible; fn construct(_app_state: &Aerosol) -> Result { std::thread::sleep(Duration::from_millis(100)); Ok(Self) } } #[test] fn obtain_non_clone() { let state = Aerosol::new(); state.obtain::>(); } }