diff --git a/Cargo.lock b/Cargo.lock index bcfb9e1..74ec424 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,9 +172,6 @@ dependencies = [ [[package]] name = "dynatos-context" version = "0.1.0" -dependencies = [ - "dynatos-world", -] [[package]] name = "dynatos-html" @@ -241,7 +238,6 @@ dependencies = [ "duplicate", "dynatos-context", "dynatos-util", - "dynatos-world", "extend", "futures", "itertools", @@ -301,14 +297,6 @@ dependencies = [ "extend", ] -[[package]] -name = "dynatos-world" -version = "0.1.0" -dependencies = [ - "derive_more", - "parking_lot", -] - [[package]] name = "either" version = "1.10.0" diff --git a/Cargo.toml b/Cargo.toml index fc9c36a..c846b4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ members = [ "dynatos-router", "dynatos-title", "dynatos-util", - "dynatos-world", ] resolver = "2" @@ -34,7 +33,6 @@ dynatos-reactive = { path = "dynatos-reactive" } dynatos-router = { path = "dynatos-router" } dynatos-title = { path = "dynatos-title" } dynatos-util = { path = "dynatos-util" } -dynatos-world = { path = "dynatos-world" } # zutil zutil-cloned = { git = "https://github.com/Zenithsiz/zutil", rev = "978fa5df733d59fc691812ce2fa6072bf901dc7f" } diff --git a/dynatos-context/Cargo.toml b/dynatos-context/Cargo.toml index aebf256..286a341 100644 --- a/dynatos-context/Cargo.toml +++ b/dynatos-context/Cargo.toml @@ -5,7 +5,5 @@ edition = "2021" [dependencies] -dynatos-world = { workspace = true } - [lints] workspace = true diff --git a/dynatos-context/src/context_stack.rs b/dynatos-context/src/context_stack.rs new file mode 100644 index 0000000..797368f --- /dev/null +++ b/dynatos-context/src/context_stack.rs @@ -0,0 +1,178 @@ +//! Context stack + +// Lints +#![expect(clippy::as_conversions, reason = "We need to unsize items and there's no other way")] + +// Imports +use { + core::{ + any::{Any, TypeId}, + cell::RefCell, + hash::BuildHasher, + marker::PhantomData, + }, + std::{collections::HashMap, hash::DefaultHasher}, +}; + +/// Context stack +// TODO: Use type with less indirections? +#[thread_local] +static CTXS_STACK: CtxsStackImpl = RefCell::new(HashMap::with_hasher(RandomState)); + +/// Handle +#[derive(Debug)] +pub struct Handle(usize, PhantomData); + +impl Clone for Handle { + fn clone(&self) -> Self { + *self + } +} +impl Copy for Handle {} + +impl !Send for Handle {} +impl !Sync for Handle {} + +/// Opaque handle +#[derive(Clone, Copy, Debug)] +pub struct OpaqueHandle { + type_id: TypeId, + idx: usize, +} + +impl !Send for OpaqueHandle {} +impl !Sync for OpaqueHandle {} + +/// Pushes a value onto the stack and returns a handle to it +pub fn push(value: T) -> Handle +where + T: Any + 'static, +{ + let mut ctxs = CTXS_STACK + .try_borrow_mut() + .expect("Cannot modify context while accessing it"); + let stack = ctxs.entry(TypeId::of::()).or_default(); + let idx = stack.len(); + stack.push(Some(Box::new(value) as Box)); + + Handle(idx, PhantomData) +} + +/// Uses the value in the top of the stack +pub fn with_top(f: F) -> O +where + T: 'static, + F: FnOnce(Option<&T>) -> O, +{ + let type_id = TypeId::of::(); + let ctxs = CTXS_STACK + .try_borrow() + .expect("Cannot access context while modifying it"); + let value = try { + let stack = ctxs.get(&type_id)?; + let value = stack.last()?.as_ref().expect("Value was already taken"); + (&**value as &dyn Any) + .downcast_ref::() + .expect("Value was the wrong type") + }; + + f(value) +} + +/// Uses the value in handle `handle`. +/// +/// # Panics +/// Panics if the context stack doesn't exist, or +/// if the value was already taken. +pub fn with(handle: Handle, f: F) -> O +where + T: 'static, + F: FnOnce(&T) -> O, +{ + let opaque_handle = self::to_opaque::(handle); + self::with_opaque(opaque_handle, |value| { + let value = (value as &dyn Any) + .downcast_ref::() + .expect("Value was the wrong type"); + f(value) + }) +} + +/// Takes the value in handle `handle` +#[expect(clippy::must_use_candidate, reason = "The user may just want to pop the value")] +pub fn take(handle: Handle) -> T +where + T: 'static, +{ + let opaque_handle = self::to_opaque::(handle); + let value = self::take_opaque(opaque_handle); + let value = (value as Box).downcast().expect("Value was the wrong type"); + + *value +} + +/// Converts a handle to an opaque handle +#[must_use] +pub fn to_opaque(handle: Handle) -> OpaqueHandle +where + T: 'static, +{ + OpaqueHandle { + type_id: TypeId::of::(), + idx: handle.0, + } +} + +/// Uses the value in handle `handle` opaquely. +/// +/// # Panics +/// Panics if the context stack doesn't exist, or +/// if the value was already taken. +pub fn with_opaque(handle: OpaqueHandle, f: F) -> O +where + F: FnOnce(&dyn Any) -> O, +{ + let ctxs = CTXS_STACK + .try_borrow() + .expect("Cannot access context while modifying it"); + let stack = ctxs.get(&handle.type_id).expect("Context stack should exist"); + let value = stack + .get(handle.idx) + .expect("Index was invalid") + .as_ref() + .expect("Value was already taken"); + f(&**value) +} + +/// Takes the value in handle `handle` opaquely +pub fn take_opaque(handle: OpaqueHandle) -> Box { + let mut ctxs = CTXS_STACK + .try_borrow_mut() + .expect("Cannot modify context while accessing it"); + let stack = ctxs.get_mut(&handle.type_id).expect("Context stack should exist"); + let value = stack + .get_mut(handle.idx) + .and_then(Option::take) + .expect("Value was already taken"); + + // Then remove any empty entries from the end + while stack.last().is_some_and(Option::is_none) { + stack.pop().expect("Should have a value at the end"); + } + + value +} + +type CtxsStackImpl = RefCell, RandomState>>; +type CtxStackImpl = Vec>>; + +/// Hash builder for the stacks +struct RandomState; + +impl BuildHasher for RandomState { + type Hasher = DefaultHasher; + + fn build_hasher(&self) -> Self::Hasher { + DefaultHasher::default() + } +} diff --git a/dynatos-context/src/lib.rs b/dynatos-context/src/lib.rs index a3bb586..8594c5f 100644 --- a/dynatos-context/src/lib.rs +++ b/dynatos-context/src/lib.rs @@ -4,37 +4,30 @@ #![feature(try_blocks, thread_local, test, negative_impls, decl_macro, unsize)] // Modules -pub mod world; - -// Exports -pub use self::world::ContextWorld; +pub mod context_stack; // Imports -use { - self::world::{ContextStack, ContextStackOpaque}, - core::{any, marker::Unsize, mem}, - dynatos_world::WorldDefault, +use core::{ + any::{self, Any}, + mem, }; /// A handle to a context value. /// /// When dropped, the context value is also dropped. #[must_use = "The handle object keeps a value in context. If dropped, the context is also dropped"] -pub struct Handle { +pub struct Handle { /// Handle - handle: world::Handle, + handle: context_stack::Handle, } -impl Handle { +impl Handle { /// Converts this handle to an opaque handle - pub fn into_opaque(self) -> OpaqueHandle - where - W::ContextStack: ContextStackOpaque, - { + pub fn into_opaque(self) -> OpaqueHandle { // Create the opaque handle and forget ourselves // Note: This is to ensure we don't try to take the value in the [`Drop`] impl let handle = OpaqueHandle { - handle: W::ContextStack::::to_opaque(self.handle), + handle: context_stack::to_opaque(self.handle), }; mem::forget(self); @@ -55,7 +48,7 @@ impl Handle { where F: FnOnce(&T) -> O, { - W::ContextStack::::with::<_, O>(self.handle, f) + context_stack::with(self.handle, f) } /// Takes the value this handle is providing a context for. @@ -71,11 +64,11 @@ impl Handle { /// Inner method for [`take`](Self::take), and the [`Drop`] impl. fn take_inner(&self) -> T { - W::ContextStack::::take(self.handle) + context_stack::take(self.handle) } } -impl Drop for Handle { +impl Drop for Handle { #[track_caller] fn drop(&mut self) { let _: T = self.take_inner(); @@ -86,23 +79,23 @@ impl Drop for Handle { /// /// When dropped, the context value is also dropped. #[must_use = "The handle object keeps a value in context. If dropped, the context is also dropped"] -pub struct OpaqueHandle { +pub struct OpaqueHandle { /// Handle - handle: world::OpaqueHandle, + handle: context_stack::OpaqueHandle, } -impl OpaqueHandle { +impl OpaqueHandle { /// Uses the value from this handle pub fn with(&self, f: F) -> O where - F: FnOnce(&world::Any) -> O, + F: FnOnce(&dyn Any) -> O, { - W::ContextStackOpaque::with_opaque(self.handle, f) + context_stack::with_opaque(self.handle, f) } /// Takes the value this handle is providing a context for. #[must_use = "If you only wish to drop the context, consider dropping the handle"] - pub fn take(self) -> Box> { + pub fn take(self) -> Box { // Get the value and forget ourselves // Note: This is to ensure we don't try to take the value in the [`Drop`] impl let value = self.take_inner(); @@ -112,31 +105,25 @@ impl OpaqueHandle { } /// Inner method for [`take`](Self::take), and the [`Drop`] impl. - fn take_inner(&self) -> Box> { - W::ContextStackOpaque::take_opaque(self.handle) + fn take_inner(&self) -> Box { + context_stack::take_opaque(self.handle) } } -impl Drop for OpaqueHandle { +impl Drop for OpaqueHandle { #[track_caller] fn drop(&mut self) { - let _: Box> = self.take_inner(); + let _: Box = self.take_inner(); } } /// Provides a value of `T` to the current context. -pub fn provide(value: T) -> Handle { - self::provide_in::(value) -} - -/// Provides a value of `T` to the current context in world `W`. -pub fn provide_in(value: T) -> Handle +pub fn provide(value: T) -> Handle where - T: Unsize>, - W: ContextWorld, + T: Any, { // Push the value onto the stack - let handle = W::ContextStack::::push(value); + let handle = context_stack::push(value); Handle { handle } } @@ -146,22 +133,12 @@ where pub fn get() -> Option where T: Copy + 'static, -{ - self::get_in::() -} - -/// Gets a value of `T` on the current context in world `W`. -#[must_use] -pub fn get_in() -> Option -where - T: Copy + 'static, - W: ContextWorld, { #[expect( clippy::redundant_closure_for_method_calls, reason = "Can't use `Option::copied` due to inference issues" )] - self::with_in::(|value| value.copied()) + self::with::(|value| value.copied()) } /// Expects a value of `T` on the current context. @@ -171,18 +148,7 @@ pub fn expect() -> T where T: Copy + 'static, { - self::expect_in::() -} - -/// Expects a value of `T` on the current context in world `W`. -#[must_use] -#[track_caller] -pub fn expect_in() -> T -where - T: Copy + 'static, - W: ContextWorld, -{ - self::with_in::(|value| *value.unwrap_or_else(self::on_missing_context::)) + self::with::(|value| *value.unwrap_or_else(self::on_missing_context::)) } /// Gets a cloned value of `T` on the current context. @@ -190,22 +156,12 @@ where pub fn get_cloned() -> Option where T: Clone + 'static, -{ - self::get_cloned_in::() -} - -/// Gets a cloned value of `T` on the current context. -#[must_use] -pub fn get_cloned_in() -> Option -where - T: Clone + 'static, - W: ContextWorld, { #[expect( clippy::redundant_closure_for_method_calls, reason = "Can't use `Option::cloned` due to inference issues" )] - self::with_in::(|value| value.cloned()) + self::with::(|value| value.cloned()) } /// Expects a cloned value of `T` on the current context. @@ -215,18 +171,7 @@ pub fn expect_cloned() -> T where T: Clone + 'static, { - self::expect_cloned_in::() -} - -/// Expects a cloned value of `T` on the current context in world `W`. -#[must_use] -#[track_caller] -pub fn expect_cloned_in() -> T -where - T: Clone + 'static, - W: ContextWorld, -{ - self::with_in::(|value| value.unwrap_or_else(self::on_missing_context::).clone()) + self::with::(|value| value.unwrap_or_else(self::on_missing_context::).clone()) } /// Uses a value of `T` on the current context. @@ -235,17 +180,7 @@ where T: 'static, F: FnOnce(Option<&T>) -> O, { - self::with_in::(f) -} - -/// Uses a value of `T` on the current context in world `W`. -pub fn with_in(f: F) -> O -where - T: 'static, - F: FnOnce(Option<&T>) -> O, - W: ContextWorld, -{ - W::ContextStack::::with_top(f) + context_stack::with_top(f) } /// Uses a value of `T` on the current context, expecting it. @@ -255,18 +190,7 @@ where T: 'static, F: FnOnce(&T) -> O, { - self::with_expect_in::(f) -} - -/// Uses a value of `T` on the current context, expecting it in world `W`. -#[track_caller] -pub fn with_expect_in(f: F) -> O -where - T: 'static, - F: FnOnce(&T) -> O, - W: ContextWorld, -{ - self::with_in::(|value| value.map(f)).unwrap_or_else(self::on_missing_context::) + self::with::(|value| value.map(f)).unwrap_or_else(self::on_missing_context::) } /// Called when context for type `T` was missing. diff --git a/dynatos-context/src/world.rs b/dynatos-context/src/world.rs deleted file mode 100644 index c5ff399..0000000 --- a/dynatos-context/src/world.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! World - -// Lints -#![expect( - type_alias_bounds, - reason = "Although they're not enforced currently, they will be in the future and we want to be explicit already" -)] - -// Modules -pub mod context_stack; - -// Exports -pub use self::context_stack::{ContextStack, ContextStackGlobal, ContextStackOpaque, ContextStackThreadLocal}; - -// Imports -use { - core::any::Any as StdAny, - dynatos_world::{World, WorldGlobal, WorldThreadLocal}, -}; - -/// Context world -pub trait ContextWorld: World { - /// Context stack - type ContextStack: ContextStack; - - /// Opaque context stack - type ContextStackOpaque: ContextStackOpaque; -} - -impl ContextWorld for WorldThreadLocal { - type ContextStack = ContextStackThreadLocal; - type ContextStackOpaque = ContextStackThreadLocal; -} - -impl ContextWorld for WorldGlobal { - type ContextStack = ContextStackGlobal; - type ContextStackOpaque = ContextStackGlobal; -} - -/// Handle type for the world's context stack -pub type Handle = as ContextStack>::Handle; - -/// Opaque handle type for the world's context stack -pub type OpaqueHandle = >::OpaqueHandle; - -/// Bounds type for the world's context stack -pub type Bounds = as ContextStack>::Bounds; - -/// Any type for the world's opaque context stack -pub type Any = >::Any; diff --git a/dynatos-context/src/world/context_stack.rs b/dynatos-context/src/world/context_stack.rs deleted file mode 100644 index 7d17a24..0000000 --- a/dynatos-context/src/world/context_stack.rs +++ /dev/null @@ -1,364 +0,0 @@ -//! Context stack - -// Lints -#![expect(clippy::as_conversions, reason = "We need to unsize items and there's no other way")] - -// Imports -use { - crate::ContextWorld, - core::{ - any::{Any, TypeId}, - hash::BuildHasher, - marker::{PhantomData, Unsize}, - }, - dynatos_world::{IMut, IMutLike, WorldGlobal, WorldThreadLocal}, - std::{collections::HashMap, hash::DefaultHasher}, -}; - -/// Context stack -pub trait ContextStack: Sized { - /// Handle - type Handle: Copy; - - /// Bounds - type Bounds: ?Sized; - - /// Pushes a value onto the stack and returns a handle to it - fn push(value: T) -> Self::Handle - where - T: Unsize + 'static; - - /// Uses the value in the top of the stack - fn with_top(f: F) -> O - where - T: 'static, - F: FnOnce(Option<&T>) -> O; - - /// Uses the value in handle `handle`. - /// - /// # Panics - /// Panics if the context stack doesn't exist, or - /// if the value was already taken. - fn with(handle: Self::Handle, f: F) -> O - where - T: 'static, - F: FnOnce(&T) -> O; - - /// Takes the value in handle `handle` - fn take(handle: Self::Handle) -> T - where - T: 'static; - - /// Converts a handle to an opaque handle - fn to_opaque(handle: Self::Handle) -> super::OpaqueHandle - where - T: 'static; -} - -/// Opaque Context stack -pub trait ContextStackOpaque: Sized { - /// Handle - type OpaqueHandle: Copy; - - /// Any type - type Any: ?Sized + Any + Unsize; - - /// Uses the value in handle `handle` opaquely. - /// - /// # Panics - /// Panics if the context stack doesn't exist, or - /// if the value was already taken. - fn with_opaque(handle: Self::OpaqueHandle, f: F) -> O - where - F: FnOnce(&Self::Any) -> O; - - /// Takes the value in handle `handle` opaquely - fn take_opaque(handle: Self::OpaqueHandle) -> Box; -} - -/// Thread-local context stack -pub struct ContextStackThreadLocal(PhantomData); - -/// Context stack for `ContextStackThreadLocal` -// TODO: Use type with less indirections? -#[thread_local] -static CTXS_STACK_THREAD_LOCAL: CtxsStackImpl = - IMut::<_, WorldThreadLocal>::new(HashMap::with_hasher(RandomState)); - -/// Handle for [`ContextStackThreadLocal`] -#[derive(Clone, Copy, Debug)] -pub struct HandleThreadLocal(usize); - -impl !Send for HandleThreadLocal {} -impl !Sync for HandleThreadLocal {} - -/// Opaque handle for [`ContextStackThreadLocal`] -#[derive(Clone, Copy, Debug)] -pub struct OpaqueHandleThreadLocal { - type_id: TypeId, - idx: usize, -} - -impl !Send for OpaqueHandleThreadLocal {} -impl !Sync for OpaqueHandleThreadLocal {} - -impl ContextStack for ContextStackThreadLocal { - type Bounds = dyn Any; - type Handle = HandleThreadLocal; - - fn push(value: T) -> Self::Handle - where - T: Unsize + 'static, - { - let idx = self::push::(&CTXS_STACK_THREAD_LOCAL, value); - HandleThreadLocal(idx) - } - - fn with_top(f: F) -> O - where - T: 'static, - F: FnOnce(Option<&T>) -> O, - { - self::with_top::(&CTXS_STACK_THREAD_LOCAL, f) - } - - fn with(handle: Self::Handle, f: F) -> O - where - T: 'static, - F: FnOnce(&T) -> O, - { - self::with::(&CTXS_STACK_THREAD_LOCAL, handle.0, f) - } - - fn take(handle: Self::Handle) -> T - where - T: 'static, - { - self::take::(&CTXS_STACK_THREAD_LOCAL, handle.0) - } - - fn to_opaque(handle: Self::Handle) -> super::OpaqueHandle - where - T: 'static, - { - OpaqueHandleThreadLocal { - type_id: TypeId::of::(), - idx: handle.0, - } - } -} - -impl ContextStackOpaque for ContextStackThreadLocal { - type Any = dyn Any; - type OpaqueHandle = OpaqueHandleThreadLocal; - - fn with_opaque(handle: Self::OpaqueHandle, f: F) -> O - where - F: FnOnce(&Self::Any) -> O, - { - self::with_opaque::(&CTXS_STACK_THREAD_LOCAL, handle.type_id, handle.idx, f) - } - - fn take_opaque(handle: Self::OpaqueHandle) -> Box { - self::take_opaque::(&CTXS_STACK_THREAD_LOCAL, handle.type_id, handle.idx) - } -} - -/// Global context stack -pub struct ContextStackGlobal(PhantomData); - -/// Context stack for `ContextStackGlobal` -// TODO: Use type with less indirections? -static CTXS_STACK_GLOBAL: CtxsStackImpl = - IMut::<_, WorldGlobal>::new(HashMap::with_hasher(RandomState)); - -/// Handle for [`ContextStackGlobal`] -#[derive(Clone, Copy, Debug)] -pub struct HandleGlobal(usize); - -/// Opaque handle for [`ContextStackGlobal`] -#[derive(Clone, Copy, Debug)] -pub struct OpaqueHandleGlobal { - type_id: TypeId, - idx: usize, -} - -impl ContextStack for ContextStackGlobal { - type Bounds = dyn Any + Send + Sync; - type Handle = HandleGlobal; - - fn push(value: T) -> Self::Handle - where - T: Unsize + 'static, - { - let idx = self::push::(&CTXS_STACK_GLOBAL, value); - HandleGlobal(idx) - } - - fn with_top(f: F) -> O - where - T: 'static, - F: FnOnce(Option<&T>) -> O, - { - self::with_top::(&CTXS_STACK_GLOBAL, f) - } - - fn with(handle: Self::Handle, f: F) -> O - where - T: 'static, - F: FnOnce(&T) -> O, - { - self::with::(&CTXS_STACK_GLOBAL, handle.0, f) - } - - fn take(handle: Self::Handle) -> T - where - T: 'static, - { - self::take::(&CTXS_STACK_GLOBAL, handle.0) - } - - fn to_opaque(handle: Self::Handle) -> super::OpaqueHandle - where - T: 'static, - { - OpaqueHandleGlobal { - type_id: TypeId::of::(), - idx: handle.0, - } - } -} - -impl ContextStackOpaque for ContextStackGlobal { - type Any = dyn Any + Send + Sync; - type OpaqueHandle = OpaqueHandleGlobal; - - fn with_opaque(handle: Self::OpaqueHandle, f: F) -> O - where - F: FnOnce(&Self::Any) -> O, - { - self::with_opaque::(&CTXS_STACK_GLOBAL, handle.type_id, handle.idx, f) - } - - fn take_opaque(handle: Self::OpaqueHandle) -> Box { - self::take_opaque::(&CTXS_STACK_GLOBAL, handle.type_id, handle.idx) - } -} - -type CtxsStackImpl = IMut, RandomState>, W>; -type CtxStackImpl = Vec>>; - -/// Hash builder for the stacks -struct RandomState; - -impl BuildHasher for RandomState { - type Hasher = DefaultHasher; - - fn build_hasher(&self) -> Self::Hasher { - DefaultHasher::default() - } -} - -fn push(ctxs_stack: &CtxsStackImpl, value: T) -> usize -where - W: ContextWorld, - A: ?Sized, - T: Unsize + 'static, -{ - let mut ctxs = ctxs_stack - .try_write() - .expect("Cannot modify context while accessing it"); - let stack = ctxs.entry(TypeId::of::()).or_default(); - let idx = stack.len(); - stack.push(Some(Box::new(value) as Box)); - - idx -} - -fn with_top(ctxs_stack: &CtxsStackImpl, f: F) -> O -where - W: ContextWorld, - A: ?Sized + Any + Unsize, - T: 'static, - F: FnOnce(Option<&T>) -> O, -{ - let type_id = TypeId::of::(); - let ctxs = ctxs_stack.try_read().expect("Cannot access context while modifying it"); - let value = try { - let stack = ctxs.get(&type_id)?; - let value = stack.last()?.as_ref().expect("Value was already taken"); - (&**value as &dyn Any) - .downcast_ref::() - .expect("Value was the wrong type") - }; - - f(value) -} - -fn with(ctxs_stack: &CtxsStackImpl, idx: usize, f: F) -> O -where - W: ContextWorld, - A: ?Sized + Any + Unsize, - T: 'static, - F: FnOnce(&T) -> O, -{ - let type_id = TypeId::of::(); - self::with_opaque::(ctxs_stack, type_id, idx, |value| { - let value = (value as &dyn Any) - .downcast_ref::() - .expect("Value was the wrong type"); - f(value) - }) -} - -fn take(ctxs_stack: &CtxsStackImpl, idx: usize) -> T -where - W: ContextWorld, - A: ?Sized + Any + Unsize, - T: 'static, -{ - let type_id = TypeId::of::(); - let value = self::take_opaque::(ctxs_stack, type_id, idx); - let value = (value as Box).downcast().expect("Value was the wrong type"); - - *value -} - - -fn with_opaque(ctxs_stack: &CtxsStackImpl, type_id: TypeId, idx: usize, f: F) -> O -where - W: ContextWorld, - A: ?Sized + 'static, - F: FnOnce(&A) -> O, -{ - let ctxs = ctxs_stack.try_read().expect("Cannot access context while modifying it"); - let stack = ctxs.get(&type_id).expect("Context stack should exist"); - let value = stack - .get(idx) - .expect("Index was invalid") - .as_ref() - .expect("Value was already taken"); - f(&**value) -} - -fn take_opaque(ctxs_stack: &CtxsStackImpl, type_id: TypeId, idx: usize) -> Box -where - W: ContextWorld, - A: ?Sized, -{ - let mut ctxs = ctxs_stack - .try_write() - .expect("Cannot modify context while accessing it"); - let stack = ctxs.get_mut(&type_id).expect("Context stack should exist"); - let value = stack - .get_mut(idx) - .and_then(Option::take) - .expect("Value was already taken"); - - // Then remove any empty entries from the end - while stack.last().is_some_and(Option::is_none) { - stack.pop().expect("Should have a value at the end"); - } - - value -} diff --git a/dynatos-loadable/src/loadable.rs b/dynatos-loadable/src/loadable.rs index 44c47c8..a1b78f2 100644 --- a/dynatos-loadable/src/loadable.rs +++ b/dynatos-loadable/src/loadable.rs @@ -8,7 +8,6 @@ use { }, dynatos_reactive::{ enum_split::{EnumSplitValue, EnumSplitValueUpdateCtx, SignalStorage}, - ReactiveWorld, Signal, SignalGetClone, SignalGetCopy, @@ -414,12 +413,11 @@ impl Default for SplitValueStorage { } } -impl EnumSplitValue for Loadable +impl EnumSplitValue for Loadable where T: Clone + 'static, E: Clone + 'static, S: SignalSet + Clone + 'static, - W: ReactiveWorld, { type SigKind = Loadable<(), ()>; type Signal = Loadable, Signal>; @@ -439,7 +437,7 @@ where self.as_ref().map(|_| ()).map_err(|_| ()) } - fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S, W>) { + fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S>) { match self { Self::Loaded(new_value) => match &storage.loaded { Some(storage) => storage.set(new_value), diff --git a/dynatos-reactive/Cargo.toml b/dynatos-reactive/Cargo.toml index 55f96bb..9888ba6 100644 --- a/dynatos-reactive/Cargo.toml +++ b/dynatos-reactive/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" dynatos-context = { workspace = true } dynatos-util = { workspace = true } -dynatos-world = { workspace = true } derive_more = { workspace = true, features = ["full"] } duplicate = { workspace = true } diff --git a/dynatos-reactive/src/async_signal.rs b/dynatos-reactive/src/async_signal.rs index 85c3187..b47e2ba 100644 --- a/dynatos-reactive/src/async_signal.rs +++ b/dynatos-reactive/src/async_signal.rs @@ -6,7 +6,6 @@ use core::panic::Location; use { crate::{ trigger::TriggerExec, - ReactiveWorld, SignalBorrow, SignalBorrowMut, SignalGetClone, @@ -21,21 +20,18 @@ use { Trigger, }, core::{ + cell::{self, RefCell}, fmt, future::Future, ops::{Deref, DerefMut}, }, - dynatos_world::{IMut, IMutLike, IMutRef, IMutRefMut, IMutRefMutLike, Rc, RcLike, WorldDefault}, futures::{future, stream::AbortHandle}, + std::rc::Rc, tokio::sync::Notify, }; -/// World for [`AsyncSignal`] -#[expect(private_bounds, reason = "We can't *not* leak some implementation details currently")] -pub trait AsyncReactiveWorld = ReactiveWorld where IMut, Self>: Sized; - /// Inner -struct Inner> { +struct Inner { /// Value value: Option, @@ -46,13 +42,13 @@ struct Inner> { handle: Option, /// Trigger - trigger: Rc, W>, + trigger: Rc, /// Notify - notify: Rc, + notify: Rc, } -impl> Inner { +impl Inner { /// Stops loading the value. /// /// Returns if the loader had a future. @@ -73,7 +69,7 @@ impl> Inner { /// /// Returns whether this created the loader's future. #[track_caller] - pub fn start_loading(&mut self, this: Rc, W>) -> bool + pub fn start_loading(&mut self, this: Rc>) -> bool where F: Loader, { @@ -92,16 +88,16 @@ impl> Inner { // Load the value // Note: If we get aborted, just remove the handle let Ok(value) = fut.await else { - this.write().handle = None; + this.borrow_mut().handle = None; return; }; // Then write it and remove the handle - let mut inner = this.write(); + let mut inner = this.borrow_mut(); inner.value = Some(value); inner.handle = None; - let trigger = inner.trigger.clone(); - let notify = Rc::<_, W>::clone(&inner.notify); + let trigger = Rc::clone(&inner.trigger); + let notify = Rc::clone(&inner.notify); drop(inner); // Finally trigger and awake all waiters. @@ -124,7 +120,7 @@ impl> Inner { /// /// Returns whether a future existed before #[track_caller] - pub fn restart_loading(&mut self, this: Rc, W>) -> bool + pub fn restart_loading(&mut self, this: Rc>) -> bool where F: Loader, { @@ -145,32 +141,23 @@ impl> Inner { } /// Async signal -pub struct AsyncSignal = WorldDefault> { +pub struct AsyncSignal { /// Inner - inner: Rc, W>, W>, + inner: Rc>>, } -impl AsyncSignal { +impl AsyncSignal { /// Creates a new async signal with a loader #[track_caller] #[must_use] pub fn new(loader: F) -> Self { - Self::new_in(loader, WorldDefault::default()) - } -} - -impl> AsyncSignal { - /// Creates a new async signal with a loader in a world - #[track_caller] - #[must_use] - pub fn new_in(loader: F, world: W) -> Self { Self { - inner: Rc::<_, W>::new(IMut::<_, W>::new(Inner { + inner: Rc::new(RefCell::new(Inner { value: None, loader, handle: None, - trigger: Rc::<_, W>::new(Trigger::new_in(world)), - notify: Rc::<_, W>::new(Notify::new()), + trigger: Rc::new(Trigger::new()), + notify: Rc::new(Notify::new()), })), } } @@ -178,8 +165,12 @@ impl> AsyncSignal { /// Stops loading the value. /// /// Returns if the loader had a future. + #[expect( + clippy::must_use_candidate, + reason = "The user may not care whether the future existed" + )] pub fn stop_loading(&self) -> bool { - self.inner.write().stop_loading() + self.inner.borrow_mut().stop_loading() } /// Starts loading the value. @@ -188,11 +179,15 @@ impl> AsyncSignal { /// /// Returns whether this created the loader's future. #[track_caller] + #[expect( + clippy::must_use_candidate, + reason = "The user may not care whether we started the future" + )] pub fn start_loading(&self) -> bool where F: Loader, { - self.inner.write().start_loading(Rc::<_, W>::clone(&self.inner)) + self.inner.borrow_mut().start_loading(Rc::clone(&self.inner)) } /// Restarts the loading. @@ -202,11 +197,15 @@ impl> AsyncSignal { /// /// Returns whether a future existed before #[track_caller] + #[expect( + clippy::must_use_candidate, + reason = "The user may not care whether the future existed" + )] pub fn restart_loading(&self) -> bool where F: Loader, { - self.inner.write().restart_loading(Rc::<_, W>::clone(&self.inner)) + self.inner.borrow_mut().restart_loading(Rc::clone(&self.inner)) } /// Returns if loading. @@ -214,14 +213,14 @@ impl> AsyncSignal { /// This is considered loading if the loader has an active future. #[must_use] pub fn is_loading(&self) -> bool { - self.inner.read().is_loading() + self.inner.borrow().is_loading() } /// Waits for the value to be loaded. /// /// If not loading, waits until the loading starts, but does not start it. - pub async fn wait(&self) -> BorrowRef<'_, F, W> { - let inner = self.inner.read(); + pub async fn wait(&self) -> BorrowRef<'_, F> { + let inner = self.inner.borrow(); self.wait_inner(inner).await } @@ -233,17 +232,23 @@ impl> AsyncSignal { /// /// If this future is dropped before completion, the loading /// will be cancelled. - pub async fn load(&self) -> BorrowRef<'_, F, W> { + pub async fn load(&self) -> BorrowRef<'_, F> { + #![expect( + clippy::await_holding_refcell_ref, + reason = "False positive, we drop it when awaiting" + )] + // If the value is loaded, return it - let mut inner = self.inner.write(); + let mut inner = self.inner.borrow_mut(); if inner.value.is_some() { - return BorrowRef(IMutRefMut::<_, W>::downgrade(inner)); + drop(inner); + return BorrowRef(self.inner.borrow()); } // Else start loading, and setup a defer to stop loading if we get cancelled. // Note: Stopping loading is a no-op if `wait` successfully returns, we only // care if we're dropped early. - let created_loader = inner.start_loading(Rc::<_, W>::clone(&self.inner)); + let created_loader = inner.start_loading(Rc::clone(&self.inner)); scopeguard::defer! { if created_loader { self.stop_loading(); @@ -251,13 +256,19 @@ impl> AsyncSignal { } // Then wait for the value - self.wait_inner(IMutRefMut::<_, W>::downgrade(inner)).await + drop(inner); + self.wait_inner(self.inner.borrow()).await } - async fn wait_inner<'a>(&'a self, mut inner: IMutRef<'a, Inner, W>) -> BorrowRef<'a, F, W> { + async fn wait_inner<'a>(&'a self, mut inner: cell::Ref<'a, Inner>) -> BorrowRef<'a, F> { + #![expect( + clippy::await_holding_refcell_ref, + reason = "False positive, we drop it when awaiting" + )] + loop { // Register a handle to be notified - let notify = Rc::<_, W>::clone(&inner.notify); + let notify = Rc::clone(&inner.notify); let notified = notify.notified(); drop(inner); @@ -266,7 +277,7 @@ impl> AsyncSignal { // Finally return the value // Note: If in the meantime the value got overwritten, we wait again - inner = self.inner.read(); + inner = self.inner.borrow(); if inner.value.is_some() { break BorrowRef(inner); } @@ -298,8 +309,8 @@ impl> AsyncSignal { /// Borrows the value, without loading it #[must_use] #[track_caller] - pub fn borrow_unloaded(&self) -> Option> { - let inner = self.inner.read(); + pub fn borrow_unloaded(&self) -> Option> { + let inner = self.inner.borrow(); inner.trigger.gather_subscribers(); inner.value.is_some().then(|| BorrowRef(inner)) } @@ -307,21 +318,21 @@ impl> AsyncSignal { /// Borrows the value, without loading it or gathering subscribers #[must_use] #[track_caller] - pub fn borrow_unloaded_raw(&self) -> Option> { - let inner = self.inner.read(); + pub fn borrow_unloaded_raw(&self) -> Option> { + let inner = self.inner.borrow(); inner.value.is_some().then(|| BorrowRef(inner)) } } -impl> Clone for AsyncSignal { +impl Clone for AsyncSignal { fn clone(&self) -> Self { Self { - inner: Rc::<_, W>::clone(&self.inner), + inner: Rc::clone(&self.inner), } } } -impl> fmt::Debug for AsyncSignal +impl fmt::Debug for AsyncSignal where F::Output: fmt::Debug, { @@ -332,9 +343,9 @@ where } /// Reference type for [`SignalBorrow`] impl -pub struct BorrowRef<'a, F: Loader, W: AsyncReactiveWorld = WorldDefault>(IMutRef<'a, Inner, W>); +pub struct BorrowRef<'a, F: Loader>(cell::Ref<'a, Inner>); -impl> fmt::Debug for BorrowRef<'_, F, W> +impl fmt::Debug for BorrowRef<'_, F> where F::Output: fmt::Debug, { @@ -343,7 +354,7 @@ where } } -impl> Deref for BorrowRef<'_, F, W> { +impl Deref for BorrowRef<'_, F> { type Target = F::Output; fn deref(&self) -> &Self::Target { @@ -351,7 +362,7 @@ impl> Deref for BorrowRef<'_, F, W> { } } -impl, W: AsyncReactiveWorld> SignalGetCopy for Option> { +impl> SignalGetCopy for Option> { type Value = Option; fn copy_value(self) -> Self::Value { @@ -359,7 +370,7 @@ impl, W: AsyncReactiveWorld> SignalGetCopy for Option } } -impl, W: AsyncReactiveWorld> SignalGetClone for Option> { +impl> SignalGetClone for Option> { type Value = Option; fn clone_value(self) -> Self::Value { @@ -367,39 +378,39 @@ impl, W: AsyncReactiveWorld> SignalGetClone for Opti } } -impl> SignalBorrow for AsyncSignal { +impl SignalBorrow for AsyncSignal { type Ref<'a> - = Option> + = Option> where Self: 'a; fn borrow(&self) -> Self::Ref<'_> { // Start loading on borrow - let mut inner = self.inner.write(); - inner.start_loading(Rc::<_, W>::clone(&self.inner)); + let mut inner = self.inner.borrow_mut(); + inner.start_loading(Rc::clone(&self.inner)); // Then get the value inner.trigger.gather_subscribers(); - inner - .value - .is_some() - .then(|| BorrowRef(IMutRefMut::<_, W>::downgrade(inner))) + inner.value.is_some().then(|| { + drop(inner); + BorrowRef(self.inner.borrow()) + }) } fn borrow_raw(&self) -> Self::Ref<'_> { // Start loading on borrow - let mut inner = self.inner.write(); - inner.start_loading(Rc::<_, W>::clone(&self.inner)); + let mut inner = self.inner.borrow_mut(); + inner.start_loading(Rc::clone(&self.inner)); // Then get the value - inner - .value - .is_some() - .then(|| BorrowRef(IMutRefMut::<_, W>::downgrade(inner))) + inner.value.is_some().then(|| { + drop(inner); + BorrowRef(self.inner.borrow()) + }) } } -impl> SignalWith for AsyncSignal +impl SignalWith for AsyncSignal where F::Output: 'static, { @@ -423,16 +434,16 @@ where } /// Reference type for [`SignalBorrowMut`] impl -pub struct BorrowRefMut<'a, F: Loader, W: AsyncReactiveWorld = WorldDefault> { +pub struct BorrowRefMut<'a, F: Loader> { /// Value - value: IMutRefMut<'a, Inner, W>, + value: cell::RefMut<'a, Inner>, /// Trigger on drop // Note: Must be dropped *after* `value`. - _trigger_on_drop: Option>, + _trigger_on_drop: Option, } -impl> fmt::Debug for BorrowRefMut<'_, F, W> +impl fmt::Debug for BorrowRefMut<'_, F> where F::Output: fmt::Debug, { @@ -441,7 +452,7 @@ where } } -impl> Deref for BorrowRefMut<'_, F, W> { +impl Deref for BorrowRefMut<'_, F> { type Target = F::Output; fn deref(&self) -> &Self::Target { @@ -449,15 +460,15 @@ impl> Deref for BorrowRefMut<'_, F, W> { } } -impl> DerefMut for BorrowRefMut<'_, F, W> { +impl DerefMut for BorrowRefMut<'_, F> { fn deref_mut(&mut self) -> &mut Self::Target { self.value.value.as_mut().expect("Borrow was `None`") } } -impl> SignalBorrowMut for AsyncSignal { +impl SignalBorrowMut for AsyncSignal { type RefMut<'a> - = Option> + = Option> where Self: 'a; @@ -465,7 +476,7 @@ impl> SignalBorrowMut for AsyncSignal // Note: We don't load when mutably borrowing, since that's probably // not what the user wants // TODO: Should we even stop loading if the value was set in the meantime? - let inner = self.inner.write(); + let inner = self.inner.borrow_mut(); // Then get the value match inner.value.is_some() { @@ -481,7 +492,7 @@ impl> SignalBorrowMut for AsyncSignal // Note: We don't load when mutably borrowing, since that's probably // not what the user wants // TODO: Should we even stop loading if the value was set in the meantime? - let inner = self.inner.write(); + let inner = self.inner.borrow_mut(); // Then get the value inner.value.is_some().then(|| BorrowRefMut { @@ -491,7 +502,7 @@ impl> SignalBorrowMut for AsyncSignal } } -impl> SignalUpdate for AsyncSignal +impl SignalUpdate for AsyncSignal where F::Output: 'static, { @@ -514,14 +525,14 @@ where } } -impl> SignalSetDefaultImpl for AsyncSignal {} -impl> SignalGetDefaultImpl for AsyncSignal {} -impl> SignalGetClonedDefaultImpl for AsyncSignal {} +impl SignalSetDefaultImpl for AsyncSignal {} +impl SignalGetDefaultImpl for AsyncSignal {} +impl SignalGetClonedDefaultImpl for AsyncSignal {} // Note: We want to return an `Option<&T>` instead of `&Option`, // so we can't use the default impl -impl> !SignalWithDefaultImpl for AsyncSignal {} -impl> !SignalUpdateDefaultImpl for AsyncSignal {} +impl !SignalWithDefaultImpl for AsyncSignal {} +impl !SignalUpdateDefaultImpl for AsyncSignal {} /// Loader pub trait Loader: 'static { diff --git a/dynatos-reactive/src/derived.rs b/dynatos-reactive/src/derived.rs index 5d59bdf..67b168c 100644 --- a/dynatos-reactive/src/derived.rs +++ b/dynatos-reactive/src/derived.rs @@ -37,7 +37,6 @@ use { Effect, EffectRun, EffectRunCtx, - ReactiveWorld, SignalBorrow, SignalGetClonedDefaultImpl, SignalGetDefaultImpl, @@ -45,25 +44,22 @@ use { Trigger, }, core::{ + cell::{self, RefCell}, fmt, marker::{PhantomData, Unsize}, ops::{CoerceUnsized, Deref}, }, - dynatos_world::{IMut, IMutLike, IMutRef, WorldDefault}, }; -/// World for [`Derived`] -pub trait DerivedWorld = ReactiveWorld where IMut, Self>: Sized; - /// Derived signal. /// /// See the module documentation for more information. -pub struct Derived = WorldDefault> { +pub struct Derived { /// Effect - effect: Effect, W>, + effect: Effect>, } -impl Derived { +impl Derived { /// Creates a new derived signal #[track_caller] pub fn new(f: F) -> Self @@ -71,41 +67,21 @@ impl Derived { T: 'static, F: Fn() -> T + 'static, { - Self::new_in(f, WorldDefault::default()) - } -} - -impl> Derived { - /// Creates a new derived signal in a world - #[track_caller] - #[expect(private_bounds, reason = "We can't *not* leak some implementation details currently")] - pub fn new_in(f: F, world: W) -> Self - where - T: 'static, - F: Fn() -> T + 'static, - EffectFn: Unsize, - { - let value = IMut::<_, W>::new(None); - let effect = Effect::new_in( - EffectFn { - trigger: Trigger::new_in(world.clone()), - value, - f, - }, - world, - ); + let value = RefCell::new(None); + let effect = Effect::new(EffectFn { + trigger: Trigger::new(), + value, + f, + }); Self { effect } } } /// Reference type for [`SignalBorrow`] impl -pub struct BorrowRef<'a, T: 'a, F: ?Sized, W: DerivedWorld = WorldDefault>( - IMutRef<'a, Option, W>, - PhantomData, -); +pub struct BorrowRef<'a, T: 'a, F: ?Sized>(cell::Ref<'a, Option>, PhantomData); -impl> Deref for BorrowRef<'_, T, F, W> { +impl Deref for BorrowRef<'_, T, F> { type Target = T; fn deref(&self) -> &Self::Target { @@ -113,15 +89,15 @@ impl> Deref for BorrowRef<'_, T, F, W> { } } -impl> fmt::Debug for BorrowRef<'_, T, F, W> { +impl fmt::Debug for BorrowRef<'_, T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (*self.0).fmt(f) } } -impl> SignalBorrow for Derived { +impl SignalBorrow for Derived { type Ref<'a> - = BorrowRef<'a, T, F, W> + = BorrowRef<'a, T, F> where Self: 'a; @@ -133,16 +109,16 @@ impl> SignalBorrow for Derived Self::Ref<'_> { let effect_fn = self.effect.inner_fn(); - let value = effect_fn.value.read(); + let value = effect_fn.value.borrow(); BorrowRef(value, PhantomData) } } -impl> SignalWithDefaultImpl for Derived {} -impl> SignalGetDefaultImpl for Derived {} -impl> SignalGetClonedDefaultImpl for Derived {} +impl SignalWithDefaultImpl for Derived {} +impl SignalGetDefaultImpl for Derived {} +impl SignalGetClonedDefaultImpl for Derived {} -impl> Clone for Derived { +impl Clone for Derived { fn clone(&self) -> Self { Self { effect: self.effect.clone(), @@ -150,49 +126,46 @@ impl> Clone for Derived { } } -impl> fmt::Debug for Derived { +impl fmt::Debug for Derived { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let effect_fn = self.effect.inner_fn(); let mut debug = f.debug_struct("Derived"); debug.field("effect", &self.effect); debug.field("trigger", &effect_fn.trigger); - match effect_fn.value.try_read() { - Some(value) => debug.field("value", &*value).finish(), - None => debug.finish_non_exhaustive(), + match effect_fn.value.try_borrow() { + Ok(value) => debug.field("value", &*value).finish(), + Err(_) => debug.finish_non_exhaustive(), } } } -impl CoerceUnsized> for Derived +impl CoerceUnsized> for Derived where F1: ?Sized + Unsize, F2: ?Sized, - W: DerivedWorld + DerivedWorld, - Effect, W>: CoerceUnsized, W>>, { } /// Effect function -struct EffectFn> { +struct EffectFn { /// Trigger - trigger: Trigger, + trigger: Trigger, /// Value - value: IMut, W>, + value: RefCell>, /// Function f: F, } -impl EffectRun for EffectFn +impl EffectRun for EffectFn where T: 'static, F: Fn() -> T, - W: DerivedWorld, { - fn run(&self, _ctx: EffectRunCtx<'_, W>) { - *self.value.write() = Some((self.f)()); + fn run(&self, _ctx: EffectRunCtx<'_>) { + *self.value.borrow_mut() = Some((self.f)()); self.trigger.exec(); } } diff --git a/dynatos-reactive/src/effect.rs b/dynatos-reactive/src/effect.rs index df8bbe3..660dc40 100644 --- a/dynatos-reactive/src/effect.rs +++ b/dynatos-reactive/src/effect.rs @@ -10,12 +10,9 @@ #[cfg(debug_assertions)] use core::panic::Location; use { - crate::{ - world::{EffectStack, ReactiveWorldInner}, - ReactiveWorld, - WeakTrigger, - }, + crate::{effect_stack, WeakTrigger}, core::{ + cell::RefCell, fmt, hash::{Hash, Hasher}, marker::{PhantomData, Unsize}, @@ -23,12 +20,14 @@ use { ptr, sync::atomic::{self, AtomicBool}, }, - dynatos_world::{IMut, IMutLike, Rc, RcLike, Weak, WeakLike, WorldDefault}, - std::collections::HashSet, + std::{ + collections::HashSet, + rc::{Rc, Weak}, + }, }; /// Effect inner -pub(crate) struct Inner { +pub(crate) struct Inner { /// Whether this effect is currently suppressed suppressed: AtomicBool, @@ -37,28 +36,34 @@ pub(crate) struct Inner { defined_loc: &'static Location<'static>, /// All dependencies of this effect - dependencies: IMut>, W>, + dependencies: RefCell>, /// Effect runner run: F, } /// Effect -pub struct Effect { +pub struct Effect { /// Inner - inner: Rc, W>, + inner: Rc>, } -impl Effect { +impl Effect { /// Creates a new computed effect. /// /// Runs the effect once to gather dependencies. #[track_caller] pub fn new(run: F) -> Self where - F: EffectRun + 'static, + F: EffectRun + 'static, { - Self::new_in(run, WorldDefault::default()) + // Create the effect + let effect = Self::new_raw(run); + + // And run it once to gather dependencies. + effect.run(); + + effect } /// Crates a new raw computed effect. @@ -67,7 +72,15 @@ impl Effect { /// dependencies manually. #[track_caller] pub fn new_raw(run: F) -> Self { - Self::new_raw_in(run, WorldDefault::default()) + let inner = Inner { + suppressed: AtomicBool::new(false), + #[cfg(debug_assertions)] + defined_loc: Location::caller(), + dependencies: RefCell::new(HashSet::new()), + run, + }; + + Self { inner: Rc::new(inner) } } /// Tries to create a new effect. @@ -76,58 +89,9 @@ impl Effect { #[track_caller] pub fn try_new(run: F) -> Option where - F: EffectRun + 'static, + F: EffectRun + 'static, { - Self::try_new_in(run, WorldDefault::default()) - } -} - -impl Effect { - /// Creates a new computed effect within a world. - /// - /// Runs the effect once to gather dependencies. - #[track_caller] - pub fn new_in(run: F, world: W) -> Self - where - F: EffectRun + Unsize + 'static, - { - // Create the effect - let effect = Self::new_raw_in(run, world); - - // And run it once to gather dependencies. - effect.run(); - - effect - } - - /// Crates a new raw computed effect within a world. - /// - /// The effect won't be run, and instead you must gather - /// dependencies manually. - #[track_caller] - pub fn new_raw_in(run: F, _world: W) -> Self { - let inner = Inner { - suppressed: AtomicBool::new(false), - #[cfg(debug_assertions)] - defined_loc: Location::caller(), - dependencies: IMut::<_, W>::new(HashSet::new()), - run, - }; - - Self { - inner: Rc::<_, W>::new(inner), - } - } - - /// Tries to create a new effect within a world. - /// - /// If the effects ends up being inert, returns `None` - #[track_caller] - pub fn try_new_in(run: F, world: W) -> Option - where - F: EffectRun + Unsize + 'static, - { - let effect = Self::new_in(run, world); + let effect = Self::new(run); match effect.is_inert() { true => None, false => Some(effect), @@ -135,7 +99,7 @@ impl Effect { } } -impl Effect { +impl Effect { /// Accesses the inner function #[must_use] pub fn inner_fn(&self) -> &F { @@ -150,9 +114,9 @@ impl Effect { /// Downgrades this effect #[must_use] - pub fn downgrade(&self) -> WeakEffect { + pub fn downgrade(&self) -> WeakEffect { WeakEffect { - inner: Rc::<_, W>::downgrade(&self.inner), + inner: Rc::downgrade(&self.inner), } } @@ -163,7 +127,7 @@ impl Effect { /// or [`WeakEffect`]s exist that point to it. #[must_use] pub fn is_inert(&self) -> bool { - Rc::<_, W>::strong_count(&self.inner) == 1 && Rc::<_, W>::weak_count(&self.inner) == 0 + Rc::strong_count(&self.inner) == 1 && Rc::weak_count(&self.inner) == 0 } /// Returns the pointer of this effect @@ -171,7 +135,7 @@ impl Effect { /// This can be used for creating maps based on equality #[must_use] pub fn inner_ptr(&self) -> *const () { - Rc::<_, W>::as_ptr(&self.inner).cast() + Rc::as_ptr(&self.inner).cast() } /// Creates an effect dependency gatherer @@ -179,12 +143,12 @@ impl Effect { /// While this type lives, all signals used will be gathered as dependencies /// for this effect. #[must_use] - pub fn deps_gatherer(&self) -> EffectDepsGatherer + pub fn deps_gatherer(&self) -> EffectDepsGatherer where - F: Unsize + 'static, + F: Unsize + 'static, { // Push the effect - W::EffectStack::push(self.clone()); + effect_stack::push(self.clone()); // Then return the gatherer, which will pop the effect from the stack on drop EffectDepsGatherer(PhantomData) @@ -195,7 +159,7 @@ impl Effect { /// All signals used within `gather` will have this effect as a dependency. pub fn gather_dependencies(&self, gather: G) -> O where - F: Unsize + 'static, + F: Unsize + 'static, G: FnOnce() -> O, { let _gatherer = self.deps_gatherer(); @@ -208,7 +172,7 @@ impl Effect { #[track_caller] pub fn run(&self) where - F: EffectRun + Unsize + 'static, + F: EffectRun + Unsize + 'static, { // If we're suppressed, don't do anything // TODO: Should we clear our dependencies in this case? @@ -220,9 +184,9 @@ impl Effect { // Clear the dependencies before running #[expect(clippy::iter_over_hash_type, reason = "We don't care about the order here")] - for dep in self.inner.dependencies.write().drain() { + for dep in self.inner.dependencies.borrow_mut().drain() { let Some(trigger) = dep.upgrade() else { continue }; - trigger.remove_subscriber(W::unsize_effect(self.clone()).downgrade()); + trigger.remove_subscriber(self.downgrade()); } // Otherwise, run it @@ -248,13 +212,14 @@ impl Effect { } /// Returns whether the effect is suppressed + #[must_use] pub fn is_suppressed(&self) -> bool { self.inner.suppressed.load(atomic::Ordering::Acquire) } /// Adds a dependency to this effect - pub(crate) fn add_dependency(&self, trigger: WeakTrigger) { - self.inner.dependencies.write().insert(trigger); + pub(crate) fn add_dependency(&self, trigger: WeakTrigger) { + self.inner.dependencies.borrow_mut().insert(trigger); } /// Formats this effect into `s` @@ -267,7 +232,7 @@ impl Effect { s.field_with("dependencies", |f| { let mut s = f.debug_list(); - let Some(deps) = self.inner.dependencies.try_read() else { + let Ok(deps) = self.inner.dependencies.try_borrow() else { return s.finish_non_exhaustive(); }; #[expect(clippy::iter_over_hash_type, reason = "We don't care about the order")] @@ -287,40 +252,38 @@ impl Effect { } } -impl PartialEq> for Effect { - fn eq(&self, other: &Effect) -> bool { +impl PartialEq> for Effect { + fn eq(&self, other: &Effect) -> bool { ptr::eq(self.inner_ptr(), other.inner_ptr()) } } -impl Eq for Effect {} +impl Eq for Effect {} -impl Clone for Effect { +impl Clone for Effect { fn clone(&self) -> Self { Self { - inner: Rc::<_, W>::clone(&self.inner), + inner: Rc::clone(&self.inner), } } } -impl Hash for Effect { +impl Hash for Effect { fn hash(&self, state: &mut H) { - Rc::<_, W>::as_ptr(&self.inner).hash(state); + Rc::as_ptr(&self.inner).hash(state); } } -impl fmt::Debug for Effect { +impl fmt::Debug for Effect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.fmt_debug(f.debug_struct("Effect")) } } -impl CoerceUnsized> for Effect +impl CoerceUnsized> for Effect where F1: ?Sized + Unsize, F2: ?Sized, - W: ReactiveWorld, - Rc, W>: CoerceUnsized, W>>, { } @@ -328,15 +291,15 @@ where /// Weak effect /// /// Used to break ownership between a signal and it's subscribers -pub struct WeakEffect { +pub struct WeakEffect { /// Inner - inner: Weak, W>, + inner: Weak>, } -impl WeakEffect { +impl WeakEffect { /// Upgrades this effect #[must_use] - pub fn upgrade(&self) -> Option> { + pub fn upgrade(&self) -> Option> { self.inner.upgrade().map(|inner| Effect { inner }) } @@ -345,16 +308,20 @@ impl WeakEffect { /// This can be used for creating maps based on equality #[must_use] pub fn inner_ptr(&self) -> *const () { - Weak::<_, W>::as_ptr(&self.inner).cast() + Weak::as_ptr(&self.inner).cast() } /// Runs this effect, if it exists. /// /// Returns if the effect still existed #[track_caller] + #[expect( + clippy::must_use_candidate, + reason = "The user may not care whether we actually ran or not" + )] pub fn try_run(&self) -> bool where - F: EffectRun + Unsize + 'static, + F: EffectRun + Unsize + 'static, { // Try to upgrade, else return that it was missing let Some(effect) = self.upgrade() else { @@ -366,30 +333,30 @@ impl WeakEffect { } } -impl PartialEq> for WeakEffect { - fn eq(&self, other: &WeakEffect) -> bool { +impl PartialEq> for WeakEffect { + fn eq(&self, other: &WeakEffect) -> bool { ptr::eq(self.inner_ptr(), other.inner_ptr()) } } -impl Eq for WeakEffect {} +impl Eq for WeakEffect {} -impl Clone for WeakEffect { +impl Clone for WeakEffect { fn clone(&self) -> Self { Self { - inner: Weak::<_, W>::clone(&self.inner), + inner: Weak::clone(&self.inner), } } } -impl Hash for WeakEffect { +impl Hash for WeakEffect { fn hash(&self, state: &mut H) { self.inner_ptr().hash(state); } } -impl fmt::Debug for WeakEffect { +impl fmt::Debug for WeakEffect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("WeakEffect"); @@ -400,12 +367,10 @@ impl fmt::Debug for WeakEffect { } } -impl CoerceUnsized> for WeakEffect +impl CoerceUnsized> for WeakEffect where F1: ?Sized + Unsize, F2: ?Sized, - W: ReactiveWorld, - Weak, W>: CoerceUnsized, W>>, { } @@ -413,45 +378,38 @@ where /// /// While this type is alive, any signals used will /// be added as a dependency. -pub struct EffectDepsGatherer<'a, W: ReactiveWorld = WorldDefault>(PhantomData<(&'a (), W)>); +pub struct EffectDepsGatherer<'a>(PhantomData<&'a ()>); -impl Drop for EffectDepsGatherer<'_, W> { +impl Drop for EffectDepsGatherer<'_> { fn drop(&mut self) { // Pop our effect from the stack - W::EffectStack::pop(); + effect_stack::pop(); } } /// Returns the current running effect #[must_use] -pub fn running() -> Option::F>> { - self::running_in::() -} - -/// Returns the current running effect in a world -#[must_use] -pub fn running_in() -> Option> { - ::EffectStack::top() +pub fn running() -> Option> { + effect_stack::top() } /// Effect run -pub trait EffectRun { +pub trait EffectRun { /// Runs the effect #[track_caller] - fn run(&self, ctx: EffectRunCtx<'_, W>); + fn run(&self, ctx: EffectRunCtx<'_>); } /// Effect run context -pub struct EffectRunCtx<'a, W: ReactiveWorld> { - _phantom: PhantomData<(&'a (), W)>, +pub struct EffectRunCtx<'a> { + _phantom: PhantomData<&'a ()>, } -impl EffectRun for F +impl EffectRun for F where F: Fn(), - W: ReactiveWorld, { - fn run(&self, _ctx: EffectRunCtx<'_, W>) { + fn run(&self, _ctx: EffectRunCtx<'_>) { self(); } } @@ -486,7 +444,7 @@ mod test { #[test] fn running() { #[thread_local] - static RUNNING: OnceCell> = OnceCell::new(); + static RUNNING: OnceCell> = OnceCell::new(); // Create an effect, and save the running effect within it to `RUNNING`. let effect = Effect::new(move || { diff --git a/dynatos-reactive/src/effect_stack.rs b/dynatos-reactive/src/effect_stack.rs new file mode 100644 index 0000000..02207d0 --- /dev/null +++ b/dynatos-reactive/src/effect_stack.rs @@ -0,0 +1,32 @@ +//! Effect stack + +// Imports +use { + crate::{Effect, EffectRun}, + core::{cell::RefCell, marker::Unsize}, +}; + +/// Effect stack impl +type EffectStackImpl = RefCell>>; + +/// Effect stack +#[thread_local] +static EFFECT_STACK: EffectStackImpl = EffectStackImpl::new(vec![]); + +/// Pushes an effect to the stack. +pub fn push(f: Effect) +where + F: ?Sized + Unsize, +{ + EFFECT_STACK.borrow_mut().push(f); +} + +/// Pops an effect from the stack +pub fn pop() { + EFFECT_STACK.borrow_mut().pop().expect("Missing added effect"); +} + +/// Returns the top effect of the stack +pub fn top() -> Option> { + EFFECT_STACK.borrow().last().cloned() +} diff --git a/dynatos-reactive/src/enum_split.rs b/dynatos-reactive/src/enum_split.rs index 744d4e0..382a222 100644 --- a/dynatos-reactive/src/enum_split.rs +++ b/dynatos-reactive/src/enum_split.rs @@ -18,7 +18,6 @@ use { Effect, EffectRun, EffectRunCtx, - ReactiveWorld, Signal, SignalBorrow, SignalGetCloned, @@ -28,49 +27,27 @@ use { SignalWithDefaultImpl, Trigger, }, - core::{fmt, marker::Unsize}, - dynatos_world::{IMut, IMutLike, WorldDefault}, + core::{cell::RefCell, fmt}, }; -/// World for [`EnumSplitSignal`] -#[expect(private_bounds, reason = "We can't *not* leak some implementation details currently")] -pub trait EnumSplitWorld> = - ReactiveWorld where IMut, Self>: Sized; - /// Enum split signal -pub struct EnumSplitSignal, W: EnumSplitWorld = WorldDefault> { +pub struct EnumSplitSignal> { /// Effect - effect: Effect, W>, + effect: Effect>, } -impl> EnumSplitSignal { +impl> EnumSplitSignal { /// Creates a new enum split signal pub fn new(signal: S) -> Self where T: 'static, S: SignalGetCloned + SignalSet + Clone + 'static, { - Self::new_in(signal, WorldDefault::default()) - } -} - -impl, W: EnumSplitWorld> EnumSplitSignal { - /// Creates a new enum split signal in a world - #[expect(private_bounds, reason = "We can't *not* leak some implementation details currently")] - pub fn new_in(signal: S, world: W) -> Self - where - T: 'static, - S: SignalGetCloned + SignalSet + Clone + 'static, - EffectFn: Unsize, - { - let effect = Effect::new_in( - EffectFn { - inner: IMut::<_, W>::new(EffectFnInner::default()), - trigger: Trigger::new(), - signal, - }, - world, - ); + let effect = Effect::new(EffectFn { + inner: RefCell::new(EffectFnInner::default()), + trigger: Trigger::new(), + signal, + }); Self { effect } } @@ -78,7 +55,8 @@ impl, W: EnumSplitWorld> EnumSplitSignal Effect, W> + #[must_use] + pub fn into_effect(self) -> Effect where S: SignalGetCloned + SignalSet + Clone + 'static, { @@ -86,7 +64,7 @@ impl, W: EnumSplitWorld> EnumSplitSignal, W: EnumSplitWorld> Clone for EnumSplitSignal { +impl> Clone for EnumSplitSignal { fn clone(&self) -> Self { Self { effect: self.effect.clone(), @@ -94,28 +72,27 @@ impl, W: EnumSplitWorld> Clone for EnumSplitSig } } -impl fmt::Debug for EnumSplitSignal +impl fmt::Debug for EnumSplitSignal where - T: EnumSplitValue, + T: EnumSplitValue, T::SignalsStorage: fmt::Debug, T::SigKind: fmt::Debug, - W: EnumSplitWorld, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("EnumSplitSignal"); s.field("effect", &self.effect); - match self.effect.inner_fn().inner.try_read() { - Some(inner) => s + match self.effect.inner_fn().inner.try_borrow() { + Ok(inner) => s .field("signals", &inner.signals) .field("cur_kind", &inner.cur_kind) .finish(), - None => s.finish_non_exhaustive(), + Err(_) => s.finish_non_exhaustive(), } } } -impl, W: EnumSplitWorld> SignalBorrow for EnumSplitSignal { +impl> SignalBorrow for EnumSplitSignal { type Ref<'a> = T::Signal where @@ -129,14 +106,14 @@ impl, W: EnumSplitWorld> SignalBorrow for EnumS fn borrow_raw(&self) -> Self::Ref<'_> { let effect_fn = self.effect.inner_fn(); - let inner = effect_fn.inner.read(); + let inner = effect_fn.inner.borrow(); let cur = inner.cur_kind.as_ref().expect("Should have a current signal"); T::get_signal(&inner.signals, cur).expect("Signal for current signal was missing") } } -impl, W: EnumSplitWorld> SignalGetCloned for EnumSplitSignal { +impl> SignalGetCloned for EnumSplitSignal { type Value = T::Signal; fn get_cloned(&self) -> Self::Value { @@ -148,15 +125,15 @@ impl, W: EnumSplitWorld> SignalGetCloned for En } } -impl, W: EnumSplitWorld> SignalWithDefaultImpl for EnumSplitSignal {} +impl> SignalWithDefaultImpl for EnumSplitSignal {} // Note: Since our `Borrow` impl doesn't return a reference, we implement // `GetCloned` manually, so we don't want the default impl -impl, W: EnumSplitWorld> !SignalGetDefaultImpl for EnumSplitSignal {} -impl, W: EnumSplitWorld> !SignalGetClonedDefaultImpl for EnumSplitSignal {} +impl> !SignalGetDefaultImpl for EnumSplitSignal {} +impl> !SignalGetClonedDefaultImpl for EnumSplitSignal {} /// Effect fn inner -struct EffectFnInner, W: ReactiveWorld> { +struct EffectFnInner> { /// Signals signals: T::SignalsStorage, @@ -164,7 +141,7 @@ struct EffectFnInner, W: ReactiveWorld> { cur_kind: Option, } -impl, W: ReactiveWorld> Default for EffectFnInner { +impl> Default for EffectFnInner { fn default() -> Self { Self { signals: T::SignalsStorage::default(), @@ -174,9 +151,9 @@ impl, W: ReactiveWorld> Default for EffectFnInner, W: EnumSplitWorld> { +struct EffectFn> { /// Inner - inner: IMut, W>, + inner: RefCell>, /// Trigger trigger: Trigger, @@ -185,18 +162,17 @@ struct EffectFn, W: EnumSplitWorld> { signal: S, } -impl EffectRun for EffectFn +impl EffectRun for EffectFn where - T: EnumSplitValue, + T: EnumSplitValue, S: SignalGetCloned + SignalSet + Clone + 'static, - W: EnumSplitWorld, { - fn run(&self, _run_ctx: EffectRunCtx<'_, W>) { + fn run(&self, _run_ctx: EffectRunCtx<'_>) { // Get the new value let new_value = self.signal.get_cloned(); // Then update the current signal - let mut inner = self.inner.write(); + let mut inner = self.inner.borrow_mut(); let prev_kind = inner.cur_kind.replace(new_value.kind()); let update_ctx = EnumSplitValueUpdateCtx::new(self.signal.clone()); new_value.update(&mut inner.signals, update_ctx); @@ -209,7 +185,7 @@ where } /// Enum split value -pub trait EnumSplitValue { +pub trait EnumSplitValue { /// Signals storage type SignalsStorage: Default; @@ -226,14 +202,13 @@ pub trait EnumSplitValue { fn kind(&self) -> Self::SigKind; /// Updates a signal with this value - fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S, W>); + fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S>); } -impl EnumSplitValue for Option +impl EnumSplitValue for Option where T: Clone + 'static, S: SignalSet + Clone + 'static, - W: ReactiveWorld, { type SigKind = Option<()>; type Signal = Option>; @@ -252,7 +227,7 @@ where self.as_ref().map(|_| ()) } - fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S, W>) { + fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S>) { let Some(new_value) = self else { return }; match storage { @@ -263,7 +238,6 @@ where } /// Extension trait to create an enum split signal -// TODO: Add this for other worlds #[extend::ext_sized(name = SignalEnumSplit)] pub impl S { /// Splits this signal into sub-signals diff --git a/dynatos-reactive/src/enum_split/ctx.rs b/dynatos-reactive/src/enum_split/ctx.rs index 27aa237..30b6c96 100644 --- a/dynatos-reactive/src/enum_split/ctx.rs +++ b/dynatos-reactive/src/enum_split/ctx.rs @@ -3,20 +3,20 @@ // Imports use { super::{EnumSplitValue, SignalStorage}, - crate::{effect, Effect, ReactiveWorld, Signal, SignalGetCloned, SignalSet, SignalWith}, + crate::{effect, Effect, Signal, SignalGetCloned, SignalSet, SignalWith}, core::marker::PhantomData, zutil_cloned::cloned, }; /// Context for [`EnumSplitValue::update`] -pub struct EnumSplitValueUpdateCtx<'a, S, W: ReactiveWorld> { +pub struct EnumSplitValueUpdateCtx<'a, S> { /// Outer signal outer_signal: S, - _phantom: PhantomData<(&'a (), W)>, + _phantom: PhantomData<&'a ()>, } -impl EnumSplitValueUpdateCtx<'_, S, W> { +impl EnumSplitValueUpdateCtx<'_, S> { /// Creates a new context. pub(crate) const fn new(outer_signal: S) -> Self { Self { @@ -33,14 +33,14 @@ impl EnumSplitValueUpdateCtx<'_, S, W> { /// created, to avoid recursion pub fn create_signal_storage(&self, value: V, into_t: F) -> SignalStorage where - T: EnumSplitValue, + T: EnumSplitValue, S: SignalSet + Clone + 'static, V: Clone + 'static, F: Fn(V) -> T + 'static, { let signal = Signal::new(value); - let cur_effect = effect::running_in::().expect("Missing running effect"); + let cur_effect = effect::running().expect("Missing running effect"); // Create the write-back effect. // Note: We don't want to run it and write into the outer at startup, so diff --git a/dynatos-reactive/src/enum_split/either.rs b/dynatos-reactive/src/enum_split/either.rs index de96dd7..7f2999a 100644 --- a/dynatos-reactive/src/enum_split/either.rs +++ b/dynatos-reactive/src/enum_split/either.rs @@ -3,7 +3,7 @@ // Imports use { super::{EnumSplitValue, EnumSplitValueUpdateCtx, SignalStorage}, - crate::{ReactiveWorld, Signal, SignalSet}, + crate::{Signal, SignalSet}, }; macro gen_either($Either:ident, $All:ident, $($t:ident: $T:ident),* $(,)?) { @@ -17,11 +17,11 @@ macro gen_either($Either:ident, $All:ident, $($t:ident: $T:ident),* $(,)?) { $( $t: $T, )* } - impl<$( $T, )* S, W> EnumSplitValue for $Either<$( $T, )*> + impl<$( $T, )* S> EnumSplitValue for $Either<$( $T, )*> where $( $T: Clone + 'static, )* S: SignalSet + Clone + 'static, - W: ReactiveWorld, + { type SigKind = $Either< $( () ${ignore($T)}, )* >; type Signal = $Either< $( Signal<$T>, )* >; @@ -47,7 +47,7 @@ macro gen_either($Either:ident, $All:ident, $($t:ident: $T:ident),* $(,)?) { } } - fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S, W>) { + fn update(self, storage: &mut Self::SignalsStorage, ctx: EnumSplitValueUpdateCtx<'_, S>) { match self { $( Self::$T(new_value) => match &storage.$t { diff --git a/dynatos-reactive/src/lib.rs b/dynatos-reactive/src/lib.rs index 29b2139..72b4f15 100644 --- a/dynatos-reactive/src/lib.rs +++ b/dynatos-reactive/src/lib.rs @@ -38,13 +38,14 @@ pub mod async_signal; pub mod derived; pub mod effect; +pub mod effect_stack; pub mod enum_split; pub mod mapped_signal; pub mod memo; +pub mod run_queue; pub mod signal; pub mod trigger; pub mod with_default; -pub mod world; // Exports pub use self::{ @@ -75,5 +76,4 @@ pub use self::{ }, trigger::{IntoSubscriber, Subscriber, Trigger, WeakTrigger}, with_default::{SignalWithDefault, WithDefault}, - world::ReactiveWorld, }; diff --git a/dynatos-reactive/src/mapped_signal.rs b/dynatos-reactive/src/mapped_signal.rs index 30d8fdf..944930c 100644 --- a/dynatos-reactive/src/mapped_signal.rs +++ b/dynatos-reactive/src/mapped_signal.rs @@ -1,30 +1,16 @@ //! Mapped signal -// TODO: Support other worlds - // Lints #![expect(type_alias_bounds, reason = "We can't use `T::Residual` without the bound")] // Imports use { - crate::{ - world::ReactiveWorldInner, - Effect, - EffectRun, - ReactiveWorld, - Signal, - SignalGetCloned, - SignalSet, - SignalUpdate, - SignalWith, - Trigger, - WeakEffect, - }, + crate::{Effect, EffectRun, Signal, SignalGetCloned, SignalSet, SignalUpdate, SignalWith, Trigger, WeakEffect}, core::{ - cell::OnceCell, + cell::{OnceCell, RefCell}, ops::{ControlFlow, FromResidual, Residual, Try}, }, - dynatos_world::{IMut, IMutLike, Rc, RcLike, WorldDefault}, + std::rc::Rc, zutil_cloned::cloned, }; @@ -76,38 +62,20 @@ where /// If you drop this signal, the relationship between /// the outer and inner signal will be broken, so keep /// this value alive while you use the inner signal -pub struct TryMappedSignal +pub struct TryMappedSignal where T: Try>>, - W: ReactiveWorld, { /// Inner - inner: Rc, W>, + inner: Rc>, } -impl TryMappedSignal +impl TryMappedSignal where T: Try>>, { /// Creates a new mapped signal from a fallible getter pub fn new(input: S, try_get: TryGet, set: Set) -> Self - where - T: 'static, - S: SignalWith + SignalUpdate + Clone + 'static, - TryGet: Fn(::Value<'_>) -> T + 'static, - Set: Fn(::Value<'_>, &T::Output) + 'static, - { - Self::new_in(input, try_get, set, WorldDefault::default()) - } -} - -impl TryMappedSignal -where - T: Try>>, - W: ReactiveWorld, -{ - /// Creates a new mapped signal from a fallible getter - pub fn new_in(input: S, try_get: TryGet, set: Set, _world: W) -> Self where T: 'static, S: SignalWith + SignalUpdate + Clone + 'static, @@ -115,7 +83,7 @@ where Set: Fn(::Value<'_>, &T::Output) + 'static, { // Output signal - let output_sig = Rc::<_, WorldDefault>::new(IMut::<_, WorldDefault>::new(None::>)); + let output_sig = Rc::new(RefCell::new(None::>)); // Trigger for gathering dependencies on retrieving the output signal, // but *not* on output signal changes. @@ -123,9 +91,7 @@ where // Weak reference to the `set_effect`, to ensure that we don't end // up with a loop and leak memory - let set_weak_effect = Rc::<_, WorldDefault>::new(OnceCell::< - WeakEffect<::F, WorldDefault>, - >::new()); + let set_weak_effect = Rc::new(OnceCell::>::new()); // The getter effect that sets the output signal #[cloned(input, output_sig, trigger, set_weak_effect)] @@ -133,7 +99,7 @@ where input.with(|input| { let value = try_get(input); - let mut output = output_sig.write(); + let mut output = output_sig.borrow_mut(); let (new_output, needs_trigger) = match value.branch() { // If the value was ok, check whether we already had a value or not ControlFlow::Continue(value) => match output.take().map(Try::branch) { @@ -192,7 +158,7 @@ where .set(set_effect.downgrade()) .expect("Set effect should be uninitialized"); - let inner = Rc::<_, W>::new(Inner { + let inner = Rc::new(Inner { output: output_sig, _get_effect: get_effect, _set_effect: set_effect, @@ -202,22 +168,21 @@ where } } -impl Clone for TryMappedSignal +impl Clone for TryMappedSignal where T: Try>>, - W: ReactiveWorld, { fn clone(&self) -> Self { Self { - inner: self.inner.clone(), + inner: Rc::clone(&self.inner), } } } -impl SignalGetCloned for TryMappedSignal +impl SignalGetCloned for TryMappedSignal where T: Try>>, - W: ReactiveWorld, + SignalTry: Clone, { type Value = SignalTry; @@ -226,7 +191,7 @@ where self.inner.trigger.gather_subscribers(); self.inner .output - .read() + .borrow() .as_ref() .expect("Output signal was missing") .clone() @@ -235,7 +200,7 @@ where fn get_cloned_raw(&self) -> Self::Value { self.inner .output - .read() + .borrow() .as_ref() .expect("Output signal was missing") .clone() @@ -243,7 +208,7 @@ where } /// Output signal type -type OutputSignal = Rc>, WorldDefault>, WorldDefault>; +type OutputSignal = Rc>>>; /// Signal try type type SignalTry = >>::TryType; @@ -257,7 +222,7 @@ where T: Try>>, F: FnOnce(&Signal), { - let mut output = output_sig.write(); + let mut output = output_sig.borrow_mut(); // Take the existing type and branch on it let new_output = match output.take().expect("Output signal was missing").branch() { @@ -274,9 +239,9 @@ where /// Mapped signal. /// /// Maps a signal, infallibly. -pub struct MappedSignal(TryMappedSignal, W>); +pub struct MappedSignal(TryMappedSignal>); -impl MappedSignal { +impl MappedSignal { /// Creates a new mapped signal from a fallible getter pub fn new(input: S, get: Get, set: Set) -> Self where @@ -285,35 +250,21 @@ impl MappedSignal { Get: Fn(::Value<'_>) -> T + 'static, Set: Fn(::Value<'_>, &T) + 'static, { - Self::new_in(input, get, set, WorldDefault::default()) - } -} - -impl MappedSignal { - /// Creates a new mapped signal from a fallible getter - pub fn new_in(input: S, get: Get, set: Set, world: W) -> Self - where - T: 'static, - S: SignalWith + SignalUpdate + Clone + 'static, - Get: Fn(::Value<'_>) -> T + 'static, - Set: Fn(::Value<'_>, &T) + 'static, - { - Self(TryMappedSignal::new_in( + Self(TryMappedSignal::new( input, move |value| Ok(get(value)), move |value, new_value| set(value, new_value), - world, )) } } -impl Clone for MappedSignal { +impl Clone for MappedSignal { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl SignalGetCloned for MappedSignal { +impl SignalGetCloned for MappedSignal { type Value = Signal; fn get_cloned(&self) -> Self::Value { @@ -326,14 +277,13 @@ impl SignalGetCloned for MappedSignal { } /// Extension trait to add a map a signal -// TODO: Add this for other worlds #[extend::ext_sized(name = SignalMapped)] pub impl S where S: SignalWith + SignalUpdate + Clone + 'static, { /// Maps this signal fallibly - fn try_mapped(self, try_get: TryGet, set: Set) -> TryMappedSignal + fn try_mapped(self, try_get: TryGet, set: Set) -> TryMappedSignal where T: Try>> + 'static, TryGet: Fn(::Value<'_>) -> T + 'static, @@ -343,7 +293,7 @@ where } /// Maps this signal - fn mapped(self, get: Get, set: Set) -> MappedSignal + fn mapped(self, get: Get, set: Set) -> MappedSignal where T: 'static, Get: Fn(::Value<'_>) -> T + 'static, diff --git a/dynatos-reactive/src/memo.rs b/dynatos-reactive/src/memo.rs index 9169ba2..0c708ac 100644 --- a/dynatos-reactive/src/memo.rs +++ b/dynatos-reactive/src/memo.rs @@ -6,7 +6,6 @@ use { Effect, EffectRun, EffectRunCtx, - ReactiveWorld, SignalBorrow, SignalGetClonedDefaultImpl, SignalGetDefaultImpl, @@ -14,25 +13,22 @@ use { Trigger, }, core::{ + cell::{self, RefCell}, fmt, marker::{PhantomData, Unsize}, ops::{CoerceUnsized, Deref}, }, - dynatos_world::{IMut, IMutLike, IMutRef, WorldDefault}, }; -/// World for [`Memo`] -pub trait MemoWorld = ReactiveWorld where IMut, Self>: Sized; - /// Memo signal. /// /// See the module documentation for more information. -pub struct Memo = WorldDefault> { +pub struct Memo { /// Effect - effect: Effect, W>, + effect: Effect>, } -impl Memo { +impl Memo { /// Creates a new memo'd signal #[track_caller] pub fn new(f: F) -> Self @@ -40,44 +36,21 @@ impl Memo { T: PartialEq + 'static, F: Fn() -> T + 'static, { - Self::new_in(f, WorldDefault::default()) - } -} - -impl> Memo { - /// Creates a new memo'd signal in a world - #[track_caller] - #[expect(private_bounds, reason = "We can't *not* leak some implementation details currently")] - pub fn new_in(f: F, world: W) -> Self - where - T: PartialEq + 'static, - F: Fn() -> T + 'static, - EffectFn: Unsize, - { - let value = IMut::<_, W>::new(None); - let effect = Effect::new_in( - EffectFn { - trigger: Trigger::new_in(world.clone()), - value, - f, - }, - world, - ); + let value = RefCell::new(None); + let effect = Effect::new(EffectFn { + trigger: Trigger::new(), + value, + f, + }); Self { effect } } } -/// World for [`BorrowRef`] -pub trait BorrowRefWorld<'a, T, F: ?Sized> = MemoWorld where IMut, Self>: 'a; - /// Reference type for [`SignalBorrow`] impl -pub struct BorrowRef<'a, T: 'a, F: ?Sized, W: BorrowRefWorld<'a, T, F> = WorldDefault>( - IMutRef<'a, Option, W>, - PhantomData, -); +pub struct BorrowRef<'a, T: 'a, F: ?Sized>(cell::Ref<'a, Option>, PhantomData); -impl<'a, T, F: ?Sized, W: BorrowRefWorld<'a, T, F>> Deref for BorrowRef<'a, T, F, W> { +impl Deref for BorrowRef<'_, T, F> { type Target = T; fn deref(&self) -> &Self::Target { @@ -85,15 +58,15 @@ impl<'a, T, F: ?Sized, W: BorrowRefWorld<'a, T, F>> Deref for BorrowRef<'a, T, F } } -impl<'a, T: fmt::Debug, F: ?Sized, W: BorrowRefWorld<'a, T, F>> fmt::Debug for BorrowRef<'a, T, F, W> { +impl fmt::Debug for BorrowRef<'_, T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (*self.0).fmt(f) } } -impl> SignalBorrow for Memo { +impl SignalBorrow for Memo { type Ref<'a> - = BorrowRef<'a, T, F, W> + = BorrowRef<'a, T, F> where Self: 'a; @@ -105,16 +78,16 @@ impl> SignalBorrow for Memo { fn borrow_raw(&self) -> Self::Ref<'_> { let effect_fn = self.effect.inner_fn(); - let value = effect_fn.value.read(); + let value = effect_fn.value.borrow(); BorrowRef(value, PhantomData) } } -impl> SignalWithDefaultImpl for Memo {} -impl> SignalGetDefaultImpl for Memo {} -impl> SignalGetClonedDefaultImpl for Memo {} +impl SignalWithDefaultImpl for Memo {} +impl SignalGetDefaultImpl for Memo {} +impl SignalGetClonedDefaultImpl for Memo {} -impl> Clone for Memo { +impl Clone for Memo { fn clone(&self) -> Self { Self { effect: self.effect.clone(), @@ -122,47 +95,44 @@ impl> Clone for Memo { } } -impl> fmt::Debug for Memo { +impl fmt::Debug for Memo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let effect_fn = self.effect.inner_fn(); let mut debug = f.debug_struct("Memo"); - match effect_fn.value.try_read() { - Some(value) => debug.field("value", &*value).finish(), - None => debug.finish_non_exhaustive(), + match effect_fn.value.try_borrow() { + Ok(value) => debug.field("value", &*value).finish(), + Err(_) => debug.finish_non_exhaustive(), } } } -impl CoerceUnsized> for Memo +impl CoerceUnsized> for Memo where F1: ?Sized + Unsize, F2: ?Sized, - W: MemoWorld + MemoWorld, - Effect, W>: CoerceUnsized, W>>, { } /// Effect function -struct EffectFn> { +struct EffectFn { /// Trigger - trigger: Trigger, + trigger: Trigger, /// Value - value: IMut, W>, + value: RefCell>, /// Function f: F, } -impl EffectRun for EffectFn +impl EffectRun for EffectFn where T: PartialEq + 'static, F: Fn() -> T, - W: MemoWorld, { - fn run(&self, _ctx: EffectRunCtx<'_, W>) { + fn run(&self, _ctx: EffectRunCtx<'_>) { let new_value = (self.f)(); - let mut value = self.value.write(); + let mut value = self.value.borrow_mut(); // Write the new value, if it's different from the previous // Note: Since we're comparing against `Some(_)`, any `None` values diff --git a/dynatos-reactive/src/run_queue.rs b/dynatos-reactive/src/run_queue.rs new file mode 100644 index 0000000..d792708 --- /dev/null +++ b/dynatos-reactive/src/run_queue.rs @@ -0,0 +1,119 @@ +//! Run queue + +// Imports +use { + crate::{trigger::SubscriberInfo, Subscriber}, + core::{ + cell::{LazyCell, RefCell}, + cmp::Reverse, + hash::{Hash, Hasher}, + }, + priority_queue::PriorityQueue, +}; + +/// Inner item for the priority queue +struct Item { + /// Subscriber + subscriber: Subscriber, + + /// Info + info: SubscriberInfo, +} + +impl PartialEq for Item { + fn eq(&self, other: &Self) -> bool { + self.subscriber == other.subscriber + } +} + +impl Eq for Item {} + +impl Hash for Item { + fn hash(&self, state: &mut H) { + self.subscriber.hash(state); + } +} + +/// Inner type for the queue impl +struct Inner { + /// Queue + // TODO: We don't need the priority, so just use some kind of + // `HashQueue`. + queue: PriorityQueue>, + + /// Next index + next: usize, + + /// Reference count + ref_count: usize, + + /// Whether currently executing the queue + is_exec: bool, +} + +impl Inner { + fn new() -> Self { + Self { + queue: PriorityQueue::new(), + next: 0, + ref_count: 0, + is_exec: false, + } + } +} + +/// Run queue +#[thread_local] +static RUN_QUEUE: LazyCell> = LazyCell::new(|| RefCell::new(Inner::new())); + +/// Execution guard +pub struct ExecGuard; + +impl Drop for ExecGuard { + fn drop(&mut self) { + let mut inner = RUN_QUEUE.borrow_mut(); + inner.is_exec = false; + } +} + +/// Increases the reference count of the queue +pub fn inc_ref() { + let mut inner = RUN_QUEUE.borrow_mut(); + inner.ref_count += 1; +} + +/// Decreases the reference count of the queue. +/// +/// Returns a guard for execution all effects if +/// this was the last trigger exec dropped. +/// +/// Any further created trigger execs won't +/// execute the functions and will instead just +/// add them to the queue +pub fn dec_ref() -> Option { + let mut inner = RUN_QUEUE.borrow_mut(); + inner.ref_count = inner + .ref_count + .checked_sub(1) + .expect("Attempted to decrease reference count beyond 0"); + + (inner.ref_count == 0 && !inner.queue.is_empty() && !inner.is_exec).then(|| { + inner.is_exec = true; + ExecGuard + }) +} + +/// Pushes a subscriber to the queue. +pub fn push(subscriber: Subscriber, info: SubscriberInfo) { + let mut inner = RUN_QUEUE.borrow_mut(); + + let next = Reverse(inner.next); + inner.queue.push_decrease(Item { subscriber, info }, next); + inner.next += 1; +} + +/// Pops a subscriber from the front of the queue +pub fn pop() -> Option<(Subscriber, SubscriberInfo)> { + let (item, _) = RUN_QUEUE.borrow_mut().queue.pop()?; + Some((item.subscriber, item.info)) +} diff --git a/dynatos-reactive/src/signal.rs b/dynatos-reactive/src/signal.rs index 6364fa8..3cb1923 100644 --- a/dynatos-reactive/src/signal.rs +++ b/dynatos-reactive/src/signal.rs @@ -28,75 +28,58 @@ pub use ops::{ // Imports use { - crate::{trigger::TriggerExec, ReactiveWorld, Trigger}, + crate::{trigger::TriggerExec, Trigger}, core::{ + cell::{self, RefCell}, fmt, marker::Unsize, mem, ops::{CoerceUnsized, Deref, DerefMut}, }, - dynatos_world::{IMut, IMutLike, IMutRef, IMutRefMut, Rc, RcLike, WorldDefault}, + std::rc::Rc, }; /// Inner -struct Inner { +struct Inner { /// Trigger - trigger: Trigger, + trigger: Trigger, /// Value - value: IMut, + value: RefCell, } /// Signal -pub struct Signal { +pub struct Signal { /// Inner - inner: Rc, W>, + inner: Rc>, } -impl Signal { - /// Creates a new signal +impl Signal { + /// Creates a new signal. #[track_caller] - pub fn new(value: T) -> Self - where - IMut: Sized, - { - Self::new_in(value, WorldDefault::default()) - } -} - -impl Signal { - /// Creates a new signal in a world. - #[track_caller] - pub fn new_in(value: T, world: W) -> Self - where - IMut: Sized, - { + pub fn new(value: T) -> Self { let inner = Inner { - value: IMut::<_, W>::new(value), - trigger: Trigger::new_in(world), + value: RefCell::new(value), + trigger: Trigger::new(), }; - Self { - inner: Rc::<_, W>::new(inner), - } + Self { inner: Rc::new(inner) } } } // TODO: Add `Signal::::downcast` once we add `{T, U}: ?Sized` to the `CoerceUnsized` impl of `Inner`. // Use `Rc::downcast::>(self.inner as Rc)` -impl CoerceUnsized> for Signal +impl CoerceUnsized> for Signal where T: ?Sized + Unsize, U: ?Sized, - W: ReactiveWorld, - Rc, W>: CoerceUnsized, W>>, { } /// Reference type for [`SignalBorrow`] impl -pub struct BorrowRef<'a, T: ?Sized + 'a, W: ReactiveWorld = WorldDefault>(IMutRef<'a, T, W>); +pub struct BorrowRef<'a, T: ?Sized + 'a>(cell::Ref<'a, T>); -impl Deref for BorrowRef<'_, T, W> { +impl Deref for BorrowRef<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -104,15 +87,15 @@ impl Deref for BorrowRef<'_, T, W> { } } -impl fmt::Debug for BorrowRef<'_, T, W> { +impl fmt::Debug for BorrowRef<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BorrowRef").field(&*self.0).finish() } } -impl SignalBorrow for Signal { +impl SignalBorrow for Signal { type Ref<'a> - = BorrowRef<'a, T, W> + = BorrowRef<'a, T> where Self: 'a; @@ -123,12 +106,12 @@ impl SignalBorrow for Signal { } fn borrow_raw(&self) -> Self::Ref<'_> { - let value = self.inner.value.read(); + let value = self.inner.value.borrow(); BorrowRef(value) } } -impl SignalReplace for Signal { +impl SignalReplace for Signal { type Value = T; fn replace(&self, new_value: T) -> Self::Value { @@ -141,16 +124,16 @@ impl SignalReplace for Signal { } /// Reference type for [`SignalBorrowMut`] impl -pub struct BorrowRefMut<'a, T: ?Sized + 'a, W: ReactiveWorld = WorldDefault> { +pub struct BorrowRefMut<'a, T: ?Sized + 'a> { /// Value - value: IMutRefMut<'a, T, W>, + value: cell::RefMut<'a, T>, /// Trigger executor // Note: Must be dropped *after* `value`. - _trigger_exec: Option>, + _trigger_exec: Option, } -impl Deref for BorrowRefMut<'_, T, W> { +impl Deref for BorrowRefMut<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -158,26 +141,26 @@ impl Deref for BorrowRefMut<'_, T, W> { } } -impl DerefMut for BorrowRefMut<'_, T, W> { +impl DerefMut for BorrowRefMut<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } } -impl fmt::Debug for BorrowRefMut<'_, T, W> { +impl fmt::Debug for BorrowRefMut<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BorrowRefMut").field(&*self.value).finish() } } -impl SignalBorrowMut for Signal { +impl SignalBorrowMut for Signal { type RefMut<'a> - = BorrowRefMut<'a, T, W> + = BorrowRefMut<'a, T> where Self: 'a; fn borrow_mut(&self) -> Self::RefMut<'_> { - let value = self.inner.value.write(); + let value = self.inner.value.borrow_mut(); BorrowRefMut { value, _trigger_exec: Some(self.inner.trigger.exec()), @@ -185,7 +168,7 @@ impl SignalBorrowMut for Signal { } fn borrow_mut_raw(&self) -> Self::RefMut<'_> { - let value = self.inner.value.write(); + let value = self.inner.value.borrow_mut(); BorrowRefMut { value, _trigger_exec: None, @@ -194,24 +177,24 @@ impl SignalBorrowMut for Signal { } -impl SignalSetDefaultImpl for Signal {} -impl SignalGetDefaultImpl for Signal {} -impl SignalGetClonedDefaultImpl for Signal {} -impl SignalWithDefaultImpl for Signal {} -impl SignalUpdateDefaultImpl for Signal {} +impl SignalSetDefaultImpl for Signal {} +impl SignalGetDefaultImpl for Signal {} +impl SignalGetClonedDefaultImpl for Signal {} +impl SignalWithDefaultImpl for Signal {} +impl SignalUpdateDefaultImpl for Signal {} -impl Clone for Signal { +impl Clone for Signal { fn clone(&self) -> Self { Self { - inner: Rc::<_, W>::clone(&self.inner), + inner: Rc::clone(&self.inner), } } } -impl fmt::Debug for Signal { +impl fmt::Debug for Signal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Signal") - .field("value", &&*self.inner.value.read()) + .field("value", &&*self.inner.value.borrow()) .field("trigger", &self.inner.trigger) .finish() } diff --git a/dynatos-reactive/src/trigger.rs b/dynatos-reactive/src/trigger.rs index c547d8e..e5bcd1f 100644 --- a/dynatos-reactive/src/trigger.rs +++ b/dynatos-reactive/src/trigger.rs @@ -5,17 +5,19 @@ // Imports use { - crate::{effect, world::RunQueue, Effect, ReactiveWorld, WeakEffect}, + crate::{effect, run_queue, Effect, EffectRun, WeakEffect}, core::{ borrow::Borrow, + cell::RefCell, fmt, hash::{Hash, Hasher}, - marker::{PhantomData, Unsize}, - ops::CoerceUnsized, + marker::Unsize, ptr, }, - dynatos_world::{IMut, IMutLike, Rc, RcLike, Weak, WeakLike, WorldDefault}, - std::collections::{hash_map, HashMap}, + std::{ + collections::{hash_map, HashMap}, + rc::{Rc, Weak}, + }, }; #[cfg(debug_assertions)] use { @@ -25,12 +27,12 @@ use { /// Subscribers #[derive(Debug)] -pub struct Subscriber { +pub struct Subscriber { /// Effect - effect: WeakEffect, + effect: WeakEffect, } -impl Clone for Subscriber { +impl Clone for Subscriber { fn clone(&self) -> Self { Self { effect: self.effect.clone(), @@ -38,22 +40,22 @@ impl Clone for Subscriber { } } -impl PartialEq for Subscriber { +impl PartialEq for Subscriber { fn eq(&self, other: &Self) -> bool { self.effect == other.effect } } -impl Eq for Subscriber {} +impl Eq for Subscriber {} -impl Hash for Subscriber { +impl Hash for Subscriber { fn hash(&self, state: &mut H) { self.effect.hash(state); } } -impl Borrow> for Subscriber { - fn borrow(&self) -> &WeakEffect { +impl Borrow> for Subscriber { + fn borrow(&self) -> &WeakEffect { &self.effect } } @@ -107,7 +109,7 @@ impl Default for SubscriberInfo { } /// Trigger inner -struct Inner { +struct Inner { /// Subscribers #[cfg_attr( not(debug_assertions), @@ -116,7 +118,7 @@ struct Inner { reason = "It isn't zero-sized with `debug_assertions`" ) )] - subscribers: IMut, SubscriberInfo>, W>, + subscribers: RefCell>, #[cfg(debug_assertions)] /// Where this trigger was defined @@ -124,25 +126,16 @@ struct Inner { } /// Trigger -pub struct Trigger { +pub struct Trigger { /// Inner - inner: Rc, W>, + inner: Rc, } -impl Trigger { +impl Trigger { /// Creates a new trigger #[must_use] #[track_caller] pub fn new() -> Self { - Self::new_in(WorldDefault::default()) - } -} - -impl Trigger { - /// Creates a new trigger in a world - #[must_use] - #[track_caller] - pub fn new_in(_world: W) -> Self { let inner = Inner { #[cfg_attr( not(debug_assertions), @@ -151,20 +144,18 @@ impl Trigger { reason = "It isn't zero-sized with `debug_assertions`" ) )] - subscribers: IMut::<_, W>::new(HashMap::new()), + subscribers: RefCell::new(HashMap::new()), #[cfg(debug_assertions)] defined_loc: Location::caller(), }; - Self { - inner: Rc::<_, W>::new(inner), - } + Self { inner: Rc::new(inner) } } /// Downgrades this trigger #[must_use] - pub fn downgrade(&self) -> WeakTrigger { + pub fn downgrade(&self) -> WeakTrigger { WeakTrigger { - inner: Rc::<_, W>::downgrade(&self.inner), + inner: Rc::downgrade(&self.inner), } } @@ -178,7 +169,7 @@ impl Trigger { // TODO: Should we remove all existing subscribers before gathering them? #[track_caller] pub fn gather_subscribers(&self) { - match effect::running_in::() { + match effect::running() { Some(effect) => { effect.add_dependency(self.downgrade()); self.add_subscriber(effect); @@ -204,8 +195,8 @@ impl Trigger { /// /// Returns if the subscriber already existed. #[track_caller] - fn add_subscriber>(&self, subscriber: S) -> bool { - let mut subscribers = self.inner.subscribers.write(); + fn add_subscriber(&self, subscriber: S) -> bool { + let mut subscribers = self.inner.subscribers.borrow_mut(); match (*subscribers).entry(subscriber.into_subscriber()) { hash_map::Entry::Occupied(mut entry) => { entry.get_mut().update(); @@ -222,14 +213,14 @@ impl Trigger { /// /// Returns if the subscriber existed #[track_caller] - pub(crate) fn remove_subscriber>(&self, subscriber: S) -> bool { + pub(crate) fn remove_subscriber(&self, subscriber: S) -> bool { Self::remove_subscriber_inner(&self.inner, subscriber) } /// Inner function for [`Self::remove_subscriber`] #[track_caller] - fn remove_subscriber_inner>(inner: &Inner, subscriber: S) -> bool { - let mut subscribers = inner.subscribers.write(); + fn remove_subscriber_inner(inner: &Inner, subscriber: S) -> bool { + let mut subscribers = inner.subscribers.borrow_mut(); subscribers.remove(&subscriber.into_subscriber()).is_some() } @@ -239,7 +230,11 @@ impl Trigger { /// executor is dropped, and there are no other executors alive, /// all queues effects are run. #[track_caller] - pub fn exec(&self) -> TriggerExec { + #[expect( + clippy::must_use_candidate, + reason = "The user can just immediately drop the value to execute if they don't care" + )] + pub fn exec(&self) -> TriggerExec { self.exec_inner( #[cfg(debug_assertions)] Location::caller(), @@ -250,11 +245,11 @@ impl Trigger { pub(crate) fn exec_inner( &self, #[cfg(debug_assertions)] exec_defined_loc: &'static Location<'static>, - ) -> TriggerExec { - let subscribers = self.inner.subscribers.read(); + ) -> TriggerExec { + let subscribers = self.inner.subscribers.borrow(); // Increase the ref count - W::RunQueue::inc_ref(); + run_queue::inc_ref(); // Then all all of our subscribers // TODO: Should we care about the order? Randomizing it is probably good, since @@ -272,7 +267,7 @@ impl Trigger { } // TODO: Should the run queue use strong effects? - W::RunQueue::push(subscriber.clone(), info.clone()); + run_queue::push(subscriber.clone(), info.clone()); } TriggerExec { @@ -280,7 +275,6 @@ impl Trigger { trigger_defined_loc: self.inner.defined_loc, #[cfg(debug_assertions)] exec_defined_loc, - _phantom: PhantomData, } } @@ -297,79 +291,79 @@ impl Trigger { } } -impl Default for Trigger { +impl Default for Trigger { fn default() -> Self { - Self::new_in(W::default()) + Self::new() } } -impl PartialEq for Trigger { +impl PartialEq for Trigger { fn eq(&self, other: &Self) -> bool { - ptr::eq(Rc::<_, W>::as_ptr(&self.inner), Rc::<_, W>::as_ptr(&other.inner)) + ptr::eq(Rc::as_ptr(&self.inner), Rc::as_ptr(&other.inner)) } } -impl Eq for Trigger {} +impl Eq for Trigger {} -impl Clone for Trigger { +impl Clone for Trigger { fn clone(&self) -> Self { Self { - inner: Rc::<_, W>::clone(&self.inner), + inner: Rc::clone(&self.inner), } } } -impl Hash for Trigger { +impl Hash for Trigger { fn hash(&self, state: &mut H) { - Rc::<_, W>::as_ptr(&self.inner).hash(state); + Rc::as_ptr(&self.inner).hash(state); } } -impl fmt::Debug for Trigger { +impl fmt::Debug for Trigger { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.fmt_debug(f.debug_struct("Trigger")) } } /// Weak trigger -pub struct WeakTrigger { +pub struct WeakTrigger { /// Inner - inner: Weak, W>, + inner: Weak, } -impl WeakTrigger { +impl WeakTrigger { /// Upgrades this weak trigger #[must_use] - pub fn upgrade(&self) -> Option> { + pub fn upgrade(&self) -> Option { let inner = self.inner.upgrade()?; Some(Trigger { inner }) } } -impl PartialEq for WeakTrigger { +impl PartialEq for WeakTrigger { fn eq(&self, other: &Self) -> bool { - ptr::eq(Weak::<_, W>::as_ptr(&self.inner), Weak::<_, W>::as_ptr(&other.inner)) + ptr::eq(Weak::as_ptr(&self.inner), Weak::as_ptr(&other.inner)) } } -impl Eq for WeakTrigger {} +impl Eq for WeakTrigger {} -impl Clone for WeakTrigger { +impl Clone for WeakTrigger { fn clone(&self) -> Self { Self { - inner: Weak::<_, W>::clone(&self.inner), + inner: Weak::clone(&self.inner), } } } -impl Hash for WeakTrigger { +impl Hash for WeakTrigger { fn hash(&self, state: &mut H) { - Weak::<_, W>::as_ptr(&self.inner).hash(state); + Weak::as_ptr(&self.inner).hash(state); } } -impl fmt::Debug for WeakTrigger { +impl fmt::Debug for WeakTrigger { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("WeakTrigger"); @@ -381,13 +375,13 @@ impl fmt::Debug for WeakTrigger { } /// Types that may be converted into a subscriber -pub trait IntoSubscriber { +pub trait IntoSubscriber { /// Converts this type into a weak effect. #[track_caller] - fn into_subscriber(self) -> Subscriber; + fn into_subscriber(self) -> Subscriber; } -impl IntoSubscriber for Subscriber { +impl IntoSubscriber for Subscriber { fn into_subscriber(self) -> Self { self } @@ -401,19 +395,17 @@ impl IntoSubscriber for Subscriber { [ &'_ Effect ] [ self.downgrade() ]; [ WeakEffect ] [ self ]; )] -impl IntoSubscriber for T +impl IntoSubscriber for T where - F: ?Sized + Unsize, - W: ReactiveWorld, - WeakEffect: CoerceUnsized>, + F: ?Sized + Unsize, { - fn into_subscriber(self) -> Subscriber { + fn into_subscriber(self) -> Subscriber { Subscriber { effect: effect_value } } } /// Trigger executor -pub struct TriggerExec { +pub struct TriggerExec { /// Trigger defined location #[cfg(debug_assertions)] trigger_defined_loc: &'static Location<'static>, @@ -421,21 +413,18 @@ pub struct TriggerExec { /// Execution defined location #[cfg(debug_assertions)] exec_defined_loc: &'static Location<'static>, - - /// Phantom - _phantom: PhantomData, } -impl Drop for TriggerExec { +impl Drop for TriggerExec { fn drop(&mut self) { // Decrease the reference count, and if we weren't the last, quit - let Some(_exec_guard) = W::RunQueue::dec_ref() else { + let Some(_exec_guard) = run_queue::dec_ref() else { return; }; // If we were the last, keep popping effects and running them until // the run queue is empty - while let Some((subscriber, info)) = W::RunQueue::pop() { + while let Some((subscriber, info)) = run_queue::pop() { let Some(effect) = subscriber.effect.upgrade() else { continue; }; diff --git a/dynatos-reactive/src/world.rs b/dynatos-reactive/src/world.rs deleted file mode 100644 index b102027..0000000 --- a/dynatos-reactive/src/world.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! World - -// Lints -#![expect( - type_alias_bounds, - reason = "Although they're not enforced currently, they will be in the future and we want to be explicit already" -)] - -// TODO: Get rid of all of the `*World` types strewn about. They only exist because we can't provide -// the necessary bounds, such as `T: Unsize => Rc: CoerceUnsized>`. - -// Modules -pub mod effect_stack; -pub mod run_queue; - -// Exports -pub use self::{ - effect_stack::{EffectStack, EffectStackGlobal, EffectStackThreadLocal}, - run_queue::{RunQueue, RunQueueGlobal, RunQueueThreadLocal}, -}; - -// Imports -use { - crate::{effect, trigger, Effect, EffectRun, WeakTrigger}, - core::{marker::Unsize, ops::CoerceUnsized}, - dynatos_world::{IMut, Weak, World, WorldGlobal, WorldThreadLocal}, - std::collections::{HashMap, HashSet}, -}; - -/// Reactive world -pub trait ReactiveWorldInner: World { - /// Effect function - type F: ?Sized + Unsize + 'static; - - /// Effect stack - type EffectStack: EffectStack; - - /// Run queue - type RunQueue: RunQueue; - - /// Unsizes an effect `Effect` to `Effect` - // TODO: Encode the capability somehow... - fn unsize_effect(effect: Effect) -> Effect - where - F: ?Sized + Unsize, - Self: ReactiveWorld; -} - -// TODO: Remove this once we can assume these bounds, or somehow encode them into `ReactiveWorldInner` -#[expect(private_bounds, reason = "We can't *not* leak some implementation details currently")] -#[cfg_attr( - not(debug_assertions), - expect( - clippy::zero_sized_map_values, - reason = "It isn't zero sized with `debug_assertions`" - ) -)] -pub trait ReactiveWorld = ReactiveWorldInner -where - ::F: EffectRun, - Weak::F, Self>, Self>: - CoerceUnsized::F, Self>, Self>>, - IMut, trigger::SubscriberInfo>, Self>: Sized, - IMut>, Self>: Sized, - IMut<(), Self>: Sized; - -impl ReactiveWorldInner for WorldThreadLocal { - type EffectStack = EffectStackThreadLocal; - type F = dyn EffectRun + 'static; - type RunQueue = RunQueueThreadLocal; - - fn unsize_effect(effect: Effect) -> Effect - where - F: ?Sized + Unsize, - Self: ReactiveWorld, - { - effect - } -} -impl ReactiveWorldInner for WorldGlobal { - type EffectStack = EffectStackGlobal; - type F = dyn EffectRun + Send + Sync + 'static; - type RunQueue = RunQueueGlobal; - - fn unsize_effect(effect: Effect) -> Effect - where - F: ?Sized + Unsize, - Self: ReactiveWorld, - { - effect - } -} diff --git a/dynatos-reactive/src/world/effect_stack.rs b/dynatos-reactive/src/world/effect_stack.rs deleted file mode 100644 index a013809..0000000 --- a/dynatos-reactive/src/world/effect_stack.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! Effect stack - -// Imports -use { - super::{ReactiveWorld, ReactiveWorldInner}, - crate::{Effect, EffectRun}, - core::marker::Unsize, - dynatos_world::{IMut, IMutLike, WorldGlobal, WorldThreadLocal}, -}; - -/// Effect stack -// TODO: Require `W: ReactiveWorld` once that doesn't result in a cycle overflow. -pub trait EffectStack: Sized { - /// Pushes an effect to the stack. - fn push(f: Effect) - where - F: ?Sized + Unsize, - W: ReactiveWorld; - - /// Pops an effect from the stack - fn pop(); - - /// Returns the top effect of the stack - fn top() -> Option> - where - W: ReactiveWorld; -} - -/// Effect stack impl -type EffectStackImpl = IMut>, W>; - -/// Thread-local effect stack, using `StdRc` and `StdRefCell` -pub struct EffectStackThreadLocal; - -/// Effect stack for `EffectStackThreadLocal` -#[thread_local] -static EFFECT_STACK_STD_RC: EffectStackImpl, WorldThreadLocal> = - EffectStackImpl::<_, WorldThreadLocal>::new(vec![]); - -impl EffectStack for EffectStackThreadLocal { - fn push(f: Effect) - where - F: ?Sized + Unsize<::F>, - { - EFFECT_STACK_STD_RC.write().push(f); - } - - fn pop() { - EFFECT_STACK_STD_RC.write().pop().expect("Missing added effect"); - } - - fn top() -> Option::F, WorldThreadLocal>> { - EFFECT_STACK_STD_RC.read().last().cloned() - } -} - -/// Global effect stack, using `StdArc` and `StdRefCell` -pub struct EffectStackGlobal; - -/// Effect stack for `EffectStackGlobal` -static EFFECT_STACK_STD_ARC: EffectStackImpl + Send + Sync, WorldGlobal> = - EffectStackImpl::<_, WorldGlobal>::new(vec![]); - - -impl EffectStack for EffectStackGlobal { - fn push(f: Effect) - where - F: ?Sized + Unsize<::F>, - { - EFFECT_STACK_STD_ARC.write().push(f); - } - - fn pop() { - EFFECT_STACK_STD_ARC.write().pop().expect("Missing added effect"); - } - - fn top() -> Option::F, WorldGlobal>> { - EFFECT_STACK_STD_ARC.read().last().cloned() - } -} diff --git a/dynatos-reactive/src/world/run_queue.rs b/dynatos-reactive/src/world/run_queue.rs deleted file mode 100644 index 60dc9df..0000000 --- a/dynatos-reactive/src/world/run_queue.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! Run queue - -// Imports -use { - super::ReactiveWorld, - crate::{trigger::SubscriberInfo, Subscriber}, - core::{ - cell::LazyCell, - cmp::Reverse, - hash::{Hash, Hasher}, - }, - dynatos_world::{IMut, IMutLike, WorldGlobal, WorldThreadLocal}, - priority_queue::PriorityQueue, - std::sync::LazyLock, -}; - -/// Run queue -// TODO: Require `W: ReactiveWorld` once that doesn't result in a cycle overflow. -pub trait RunQueue: Sized { - /// Exec guard - type ExecGuard; - - /// Increases the reference count of the queue - fn inc_ref(); - - /// Decreases the reference count of the queue. - /// - /// Returns a guard for execution all effects if - /// this was the last trigger exec dropped. - /// - /// Any further created trigger execs won't - /// execute the functions and will instead just - /// add them to the queue - fn dec_ref() -> Option; - - /// Pushes a subscriber to the queue. - fn push(subscriber: Subscriber, info: SubscriberInfo) - where - W: ReactiveWorld; - - /// Pops a subscriber from the front of the queue - fn pop() -> Option<(Subscriber, SubscriberInfo)> - where - W: ReactiveWorld; -} - -/// Inner item for the priority queue -struct Item { - /// Subscriber - subscriber: Subscriber, - - /// Info - info: SubscriberInfo, -} - -impl PartialEq for Item { - fn eq(&self, other: &Self) -> bool { - self.subscriber == other.subscriber - } -} - -impl Eq for Item {} - -impl Hash for Item { - fn hash(&self, state: &mut H) { - self.subscriber.hash(state); - } -} - -/// Inner type for the queue impl -struct Inner { - /// Queue - // TODO: We don't need the priority, so just use some kind of - // `HashQueue`. - queue: PriorityQueue, Reverse>, - - /// Next index - next: usize, - - /// Reference count - ref_count: usize, - - /// Whether currently executing the queue - is_exec: bool, -} - -impl Inner { - fn new() -> Self { - Self { - queue: PriorityQueue::new(), - next: 0, - ref_count: 0, - is_exec: false, - } - } -} - -/// Run queue impl -type RunQueueImpl = IMut, W>; - -/// Thread-local run queue, using `StdRc` and `StdRefCell` -pub struct RunQueueThreadLocal; - -/// Run queue for `RunQueueThreadLocal` -#[thread_local] -static RUN_QUEUE_STD_RC: LazyCell> = - LazyCell::new(|| RunQueueImpl::::new(Inner::new())); - -/// `RunQueueThreadLocal` execution guard -pub struct RunQueueExecGuardThreadLocal; - -impl Drop for RunQueueExecGuardThreadLocal { - fn drop(&mut self) { - let mut inner = RUN_QUEUE_STD_RC.write(); - inner.is_exec = false; - } -} - -impl RunQueue for RunQueueThreadLocal { - type ExecGuard = RunQueueExecGuardThreadLocal; - - fn inc_ref() { - let mut inner = RUN_QUEUE_STD_RC.write(); - inner.ref_count += 1; - } - - fn dec_ref() -> Option { - let mut inner = RUN_QUEUE_STD_RC.write(); - inner.ref_count = inner - .ref_count - .checked_sub(1) - .expect("Attempted to decrease reference count beyond 0"); - - (inner.ref_count == 0 && !inner.queue.is_empty() && !inner.is_exec).then(|| { - inner.is_exec = true; - RunQueueExecGuardThreadLocal - }) - } - - fn push(subscriber: Subscriber, info: SubscriberInfo) { - let mut inner = RUN_QUEUE_STD_RC.write(); - - let next = Reverse(inner.next); - inner.queue.push_decrease(Item { subscriber, info }, next); - inner.next += 1; - } - - fn pop() -> Option<(Subscriber, SubscriberInfo)> - where - WorldThreadLocal: ReactiveWorld, - { - let (item, _) = RUN_QUEUE_STD_RC.write().queue.pop()?; - Some((item.subscriber, item.info)) - } -} - -/// Global run queue, using `StdArc` and `StdRefCell` -pub struct RunQueueGlobal; - -/// Run queue for `RunQueueGlobal` -static RUN_QUEUE_STD_ARC: LazyLock> = - LazyLock::new(|| RunQueueImpl::::new(Inner::new())); - -/// `RunQueueGlobal` execution guard -pub struct RunQueueExecGuardGlobal; - -impl Drop for RunQueueExecGuardGlobal { - fn drop(&mut self) { - let mut inner = RUN_QUEUE_STD_ARC.write(); - assert!(inner.is_exec, "Run queue stopped execution before guard was dropped"); - inner.is_exec = false; - } -} - -impl RunQueue for RunQueueGlobal { - type ExecGuard = RunQueueExecGuardGlobal; - - fn inc_ref() { - let mut inner = RUN_QUEUE_STD_ARC.write(); - inner.ref_count += 1; - } - - fn dec_ref() -> Option { - let mut inner = RUN_QUEUE_STD_ARC.write(); - inner.ref_count = inner - .ref_count - .checked_sub(1) - .expect("Attempted to decrease reference count beyond 0"); - - (inner.ref_count == 0 && !inner.queue.is_empty() && !inner.is_exec).then(|| { - inner.is_exec = true; - RunQueueExecGuardGlobal - }) - } - - fn push(subscriber: Subscriber, info: SubscriberInfo) { - let mut inner = RUN_QUEUE_STD_ARC.write(); - - let next = Reverse(inner.next); - inner.queue.push_decrease(Item { subscriber, info }, next); - inner.next += 1; - } - - fn pop() -> Option<(Subscriber, SubscriberInfo)> - where - WorldGlobal: ReactiveWorld, - { - let (item, _) = RUN_QUEUE_STD_ARC.write().queue.pop()?; - Some((item.subscriber, item.info)) - } -} diff --git a/dynatos-world/Cargo.toml b/dynatos-world/Cargo.toml deleted file mode 100644 index ce68626..0000000 --- a/dynatos-world/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "dynatos-world" -version = "0.1.0" -edition = "2021" - -[dependencies] - -derive_more = { workspace = true, features = ["full"] } -parking_lot = { workspace = true } - -[lints] -workspace = true diff --git a/dynatos-world/src/imut.rs b/dynatos-world/src/imut.rs deleted file mode 100644 index 82a340b..0000000 --- a/dynatos-world/src/imut.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Inner-mutability types - -// Imports -use core::{ - cell::{self, RefCell}, - ops, -}; - -/// Inner mutability family -pub trait IMutFamily: Sized { - /// Returns the inner mutability type of `T` - type IMut: ?Sized + IMutLike; -} - -/// Inner mutability-like -pub trait IMutLike { - /// Reference - type Ref<'a>: IMutRefLike<'a, T, IMut = Self> - where - T: 'a, - Self: 'a; - - /// Mutable reference - type RefMut<'a>: IMutRefMutLike<'a, T, IMut = Self> - where - T: 'a, - Self: 'a; - - /// Creates a new value - fn new(value: T) -> Self - where - T: Sized; - - /// Gets a read-lock to this value - fn read(&self) -> Self::Ref<'_>; - - /// Gets a write-lock to this value - fn write(&self) -> Self::RefMut<'_>; - - /// Tries to get a read-lock to this value - fn try_read(&self) -> Option>; - - /// Tries to get a write-lock to this value - fn try_write(&self) -> Option>; -} - -/// Inner mutability immutable reference like -pub trait IMutRefLike<'a, T: ?Sized + 'a>: 'a + ops::Deref { - /// The [`IMutLike`] of this type - type IMut: ?Sized + IMutLike = Self>; -} - -/// Inner mutability mutable reference like -pub trait IMutRefMutLike<'a, T: ?Sized + 'a>: 'a + ops::DerefMut { - /// The [`IMutLike`] of this type - type IMut: ?Sized + IMutLike = Self>; - - /// Downgrades this to a [`IMutRefLike`] - fn downgrade(this: Self) -> >::Ref<'a>; -} - -/// Refcell family of inner-mutability -pub struct StdRefcell; - -impl IMutFamily for StdRefcell { - type IMut = RefCell; -} - -impl IMutLike for RefCell { - type Ref<'a> - = cell::Ref<'a, T> - where - Self: 'a; - type RefMut<'a> - = RefCellRefMut<'a, T> - where - Self: 'a; - - fn new(value: T) -> Self - where - T: Sized, - { - Self::new(value) - } - - #[track_caller] - fn read(&self) -> Self::Ref<'_> { - self.borrow() - } - - #[track_caller] - fn write(&self) -> Self::RefMut<'_> { - RefCellRefMut { - borrow: self.borrow_mut(), - refcell: self, - } - } - - fn try_read(&self) -> Option> { - self.try_borrow().ok() - } - - fn try_write(&self) -> Option> { - let borrow = self.try_borrow_mut().ok()?; - Some(RefCellRefMut { borrow, refcell: self }) - } -} - -impl<'a, T: ?Sized> IMutRefLike<'a, T> for cell::Ref<'a, T> { - type IMut = RefCell; -} - -/// Wrapper around `core::cell::RefMut`. -#[derive(derive_more::Deref, derive_more::DerefMut, derive_more::Debug)] -#[debug("{borrow:?}")] -pub struct RefCellRefMut<'a, T: ?Sized> { - /// Borrow - #[deref(forward)] - #[deref_mut] - borrow: cell::RefMut<'a, T>, - - /// Original refcell - // Note: This field is necessary for downgrading. - refcell: &'a RefCell, -} - -impl<'a, T: ?Sized> IMutRefMutLike<'a, T> for RefCellRefMut<'a, T> { - type IMut = RefCell; - - fn downgrade(this: Self) -> >::Ref<'a> { - // Note: RefCell is single threaded, so there are no races here - drop(this.borrow); - this.refcell.borrow() - } -} - -/// `parking_lot::RwLock` family of inner-mutability -pub struct ParkingLotRwLock; - -impl IMutFamily for ParkingLotRwLock { - type IMut = parking_lot::RwLock; -} - -impl IMutLike for parking_lot::RwLock { - type Ref<'a> - = parking_lot::RwLockReadGuard<'a, T> - where - Self: 'a; - type RefMut<'a> - = parking_lot::RwLockWriteGuard<'a, T> - where - Self: 'a; - - fn new(value: T) -> Self - where - T: Sized, - { - Self::new(value) - } - - #[track_caller] - fn read(&self) -> Self::Ref<'_> { - self.read() - } - - #[track_caller] - fn write(&self) -> Self::RefMut<'_> { - self.write() - } - - fn try_read(&self) -> Option> { - self.try_read() - } - - fn try_write(&self) -> Option> { - self.try_write() - } -} - -impl<'a, T: ?Sized> IMutRefLike<'a, T> for parking_lot::RwLockReadGuard<'a, T> { - type IMut = parking_lot::RwLock; -} - -impl<'a, T: ?Sized> IMutRefMutLike<'a, T> for parking_lot::RwLockWriteGuard<'a, T> { - type IMut = parking_lot::RwLock; - - fn downgrade(this: Self) -> >::Ref<'a> { - Self::downgrade(this) - } -} diff --git a/dynatos-world/src/lib.rs b/dynatos-world/src/lib.rs deleted file mode 100644 index 61c00e6..0000000 --- a/dynatos-world/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! `dynatos`'s world types. - -// Features -#![feature( - unsize, - coerce_unsized, - unboxed_closures, - fn_traits, - test, - thread_local, - trait_alias, - once_cell_try, - async_fn_traits, - local_waker -)] -// Lints -#![expect( - type_alias_bounds, - reason = "Although they're not enforced currently, they will be in the future and we want to be explicit already" -)] - -// TODO: Get rid of all of the `*World` types strewn about. They only exist because we can't provide -// the necessary bounds, such as `T: Unsize => Rc: CoerceUnsized>`. - -// Modules -pub mod imut; -pub mod rc; - -// Exports -pub use self::{ - imut::{IMutFamily, IMutLike, IMutRefLike, IMutRefMutLike, ParkingLotRwLock, StdRefcell}, - rc::{RcFamily, RcLike, StdArc, StdRc, WeakLike}, -}; - -/// World -pub trait World: Sized + Clone + 'static { - /// Reference-counted pointer family - type Rc: RcFamily; - - /// Inner mutability family - type IMut: IMutFamily; -} - -/// Thread-local world -#[derive(Clone, Copy, Default)] -pub struct WorldThreadLocal; - -impl World for WorldThreadLocal { - type IMut = StdRefcell; - type Rc = StdRc; -} - -/// Global world -#[derive(Clone, Copy, Default)] -pub struct WorldGlobal; - -impl World for WorldGlobal { - type IMut = ParkingLotRwLock; - type Rc = StdArc; -} - -/// The `Rc` of the world `W` -pub type Rc = ::Rc; - -/// The `Weak` of the world `W` -pub type Weak = ::Weak; - -/// The `IMut` of the world `W` -pub type IMut = ::IMut; - -/// The `IMutRef` of the world `W` -pub type IMutRef<'a, T: ?Sized + 'a, W: World> = as IMutLike>::Ref<'a>; - -/// The `IMutRefMut` of the world `W` -pub type IMutRefMut<'a, T: ?Sized + 'a, W: World> = as IMutLike>::RefMut<'a>; - -/// Default world -pub type WorldDefault = WorldThreadLocal; diff --git a/dynatos-world/src/rc.rs b/dynatos-world/src/rc.rs deleted file mode 100644 index 4a496ac..0000000 --- a/dynatos-world/src/rc.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! Reference-counted pointer - -// Imports -use { - core::ops, - std::{rc, sync}, -}; - -/// Reference-counted pointer family -pub trait RcFamily: Sized { - /// Returns the reference counted type of `T` - type Rc: RcLike; - - /// Weak type - type Weak: WeakLike; -} - -/// A reference-counted pointer -pub trait RcLike: ops::Deref + Clone { - /// The family of this pointer - type Family: RcFamily = Self>; - - /// Creates a new Rc from a value - fn new(value: T) -> Self - where - T: Sized; - - /// Downgrades this Rc to a Weak - fn downgrade(this: &Self) -> ::Weak; - - /// Gets a pointer to the inner data of this Rc - fn as_ptr(this: &Self) -> *const T; - - /// Returns the strong count of this Rc - fn strong_count(this: &Self) -> usize; - - /// Returns the weak count of this Rc - fn weak_count(this: &Self) -> usize; -} - -/// A Reference-counted weak pointer -pub trait WeakLike: Clone { - /// The family of this pointer - type Family: RcFamily = Self>; - - /// Upgrades this weak to an rc - fn upgrade(&self) -> Option<::Rc>; - - /// Gets a pointer to the inner data of this rc - fn as_ptr(&self) -> *const T; -} - -/// Arc family of reference-counter pointers -pub struct StdArc; - -impl RcFamily for StdArc { - type Rc = sync::Arc; - type Weak = sync::Weak; -} - -impl RcLike for sync::Arc { - type Family = StdArc; - - fn new(value: T) -> Self - where - T: Sized, - { - Self::new(value) - } - - fn downgrade(this: &Self) -> ::Weak { - Self::downgrade(this) - } - - fn as_ptr(this: &Self) -> *const T { - Self::as_ptr(this) - } - - fn strong_count(this: &Self) -> usize { - Self::strong_count(this) - } - - fn weak_count(this: &Self) -> usize { - Self::weak_count(this) - } -} - -impl WeakLike for sync::Weak { - type Family = StdArc; - - fn upgrade(&self) -> Option<::Rc> { - self.upgrade() - } - - fn as_ptr(&self) -> *const T { - self.as_ptr() - } -} - -/// Rc family of reference-counter pointers -pub struct StdRc; - -impl RcFamily for StdRc { - type Rc = rc::Rc; - type Weak = rc::Weak; -} - -impl RcLike for rc::Rc { - type Family = StdRc; - - fn new(value: T) -> Self - where - T: Sized, - { - Self::new(value) - } - - fn downgrade(this: &Self) -> ::Weak { - Self::downgrade(this) - } - - fn as_ptr(this: &Self) -> *const T { - Self::as_ptr(this) - } - - fn strong_count(this: &Self) -> usize { - Self::strong_count(this) - } - - fn weak_count(this: &Self) -> usize { - Self::weak_count(this) - } -} - -impl WeakLike for rc::Weak { - type Family = StdRc; - - fn upgrade(&self) -> Option<::Rc> { - self.upgrade() - } - - fn as_ptr(&self) -> *const T { - self.as_ptr() - } -} diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 7cb5d17..8ba00fc 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -194,9 +194,6 @@ dependencies = [ [[package]] name = "dynatos-context" version = "0.1.0" -dependencies = [ - "dynatos-world", -] [[package]] name = "dynatos-html" @@ -263,7 +260,6 @@ dependencies = [ "duplicate", "dynatos-context", "dynatos-util", - "dynatos-world", "extend", "futures", "itertools", @@ -307,14 +303,6 @@ dependencies = [ "extend", ] -[[package]] -name = "dynatos-world" -version = "0.1.0" -dependencies = [ - "derive_more", - "parking_lot", -] - [[package]] name = "either" version = "1.10.0"