From 9b70b0f7bc68cf878ff84d9dbfa1539d1b91a089 Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Sun, 25 Feb 2024 23:40:52 +0100 Subject: [PATCH] working signal reactivity --- mangades/src/component_demo_syntax.rs | 3 +- rusalka-macro/src/lib.rs | 28 +++--- rusalka/src/store/mod.rs | 120 ++++++++++++++------------ 3 files changed, 83 insertions(+), 68 deletions(-) diff --git a/mangades/src/component_demo_syntax.rs b/mangades/src/component_demo_syntax.rs index b4ca708..d8e9700 100644 --- a/mangades/src/component_demo_syntax.rs +++ b/mangades/src/component_demo_syntax.rs @@ -20,8 +20,7 @@ make_component!( test_: bool = false } Reactive { - println!("reactive block"); - println!("test_ = {}", $test_); + dbg!($test_); } Component { @layout { diff --git a/rusalka-macro/src/lib.rs b/rusalka-macro/src/lib.rs index 9c35722..a7c93e5 100644 --- a/rusalka-macro/src/lib.rs +++ b/rusalka-macro/src/lib.rs @@ -316,9 +316,9 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream for variable in &reactive_variables { component_struct_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); component_struct_stream.extend(TokenStream::from(quote!(:))); - component_struct_stream.extend(TokenStream::from(quote!(std::sync::Arc>>))); + component_struct_stream.extend(TokenStream::from(quote!(>>))); component_struct_stream.extend(TokenStream::from(quote!(,))); } @@ -380,9 +380,9 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream for attribute in &attributes { attributes_default_struct_stream.extend(TokenStream::from(quote!(pub))); attributes_default_struct_stream.extend(Some(TokenTree::Ident(attribute.name.clone()))); - attributes_default_struct_stream.extend(TokenStream::from(quote!(: std::sync::Arc>>,))); + attributes_default_struct_stream.extend(TokenStream::from(quote!(>>,))); } output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_default_struct_stream)))); @@ -450,7 +450,7 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream for attribute in &attributes { let name = attribute.name.clone(); from_fn_stream_inner.extend(TokenStream::from(quote!( - #name : std::sync::Arc::new(std::sync::Mutex::new(rusalka::store::Writable::new(attrs.#name))) + #name : std::sync::Arc::new(rusalka::store::Writable::new(attrs.#name)) ))); } @@ -500,8 +500,8 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream invalidator_inner.extend(TokenStream::from(quote!(Default::default()))); } new_stream.extend(TokenStream::from(quote!( - let #name : std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(rusalka::store::Writable::new( #invalidator_inner ))); + let #name : std::sync::Arc> = + std::sync::Arc::new(rusalka::store::Writable::new( #invalidator_inner )); ))); } @@ -568,13 +568,19 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream let ident = format_ident!("sub{}", i); let content = &reactive_block.contents; let variables = &reactive_block.variables; + let vecvariables = reactive_block.variables.iter().map(|v| format_ident!("vec{}", v)); + let vecvariables2 = reactive_block.variables.iter().map(|v| format_ident!("vec{}", v)); new_returnvalue_stream.extend(quote!( #ident: { #(let #variables = #variables.clone();)* - [#(#variables.clone().lock().unwrap()),*].subscribe(Box::new(move || { + #(let #vecvariables = #variables.clone();)* + let vec = [#(#vecvariables2),*]; + let res = vec.subscribe(Box::new(move || { #(let #variables = #variables.clone();)* #content - })) + })); + drop(vec); + res }, )); i+=1; @@ -675,7 +681,7 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream set_stream_inner.extend(TokenStream::from(quote!(self.attrs.))); set_stream_inner.extend(Some(TokenTree::Ident(attribute.name.clone()))); - set_stream_inner.extend(TokenStream::from(quote!(.lock().unwrap().set))); + set_stream_inner.extend(TokenStream::from(quote!(.set))); let mut set_stream_inner_inner = TokenStream::new(); @@ -869,7 +875,7 @@ fn replace_variables(stream: TokenStream) -> (Vec, TokenStream) { idents.push(ident.clone()); output.extend(TokenStream::from(quote!(**))); output.extend(Some(TokenTree::Ident(ident.clone()))); - output.extend(TokenStream::from(quote!(.lock().unwrap().guard()))); + output.extend(TokenStream::from(quote!(.guard()))); }, TokenTree::Group(group) => { let group_delim = group.delimiter(); diff --git a/rusalka/src/store/mod.rs b/rusalka/src/store/mod.rs index c0b5a02..36debc7 100644 --- a/rusalka/src/store/mod.rs +++ b/rusalka/src/store/mod.rs @@ -1,4 +1,5 @@ use std::ops::{Deref, DerefMut}; +use std::ptr::drop_in_place; use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak, MutexGuard}; @@ -11,11 +12,11 @@ pub trait Signal { pub trait ReadableStore: Signal { type State; - fn get(&self) -> &Self::State; + fn get(&self) -> MutexGuard; } pub trait WritableStore: ReadableStore { - fn set(&mut self, state: Self::State); + fn set(&self, state: Self::State); } struct Listener { @@ -38,54 +39,54 @@ impl Drop for ReadableUnsubscribe { impl StoreUnsubscribe for ReadableUnsubscribe {} -pub struct Readable { - state: T, - listeners: Arc>> -} - -impl Readable { - pub fn new(state: T) -> Self { - Self { - state, - listeners: Arc::new(Mutex::new(Vec::new())) - } - } -} +// this isn't actually usable as of now, so it's commented out until I think of a better way to do it +// pub struct Readable { +// state: T, +// listeners: Arc>> +// } +// +// impl Readable { +// pub fn new(state: T) -> Self { +// Self { +// state, +// listeners: Arc::new(Mutex::new(Vec::new())) +// } +// } +// } static CALL_COUNT: AtomicU64 = AtomicU64::new(0); -impl Signal for Readable { - fn subscribe(&self, mut callback: Box) -> Box { - let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - let mut listeners = self.listeners.lock().unwrap(); - callback(); - listeners.push(Listener { - callback, - hash - }); - Box::new(ReadableUnsubscribe { - listeners: Arc::downgrade(&self.listeners), - hash - }) - } -} - -impl ReadableStore for Readable { - type State = T; - fn get(&self) -> &Self::State { - &self.state - } -} +// impl Signal for Readable { +// fn subscribe(&self, mut callback: Box) -> Box { +// let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); +// let mut listeners = self.listeners.lock().unwrap(); +// listeners.push(Listener { +// callback, +// hash +// }); +// Box::new(ReadableUnsubscribe { +// listeners: Arc::downgrade(&self.listeners), +// hash +// }) +// } +// } +// +// impl ReadableStore for Readable { +// type State = T; +// fn get(&self) -> &Self::State { +// &self.state +// } +// } pub struct Writable { - state: T, + state: Mutex, listeners: Arc>> } impl Writable { pub fn new(state: T) -> Self { Self { - state, + state: Mutex::new(state), listeners: Arc::new(Mutex::new(Vec::new())) } } @@ -95,7 +96,6 @@ impl Signal for Writable { fn subscribe(&self, mut callback: Box) -> Box { let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let mut listeners = self.listeners.lock().unwrap(); - callback(); listeners.push(Listener { callback, hash @@ -109,14 +109,14 @@ impl Signal for Writable { impl ReadableStore for Writable { type State = T; - fn get(&self) -> &Self::State { - &self.state + fn get(&self) -> MutexGuard { + self.state.lock().unwrap() } } impl WritableStore for Writable { - fn set(&mut self, state: Self::State) { - self.state = state; + fn set(&self, state: Self::State) { + *self.state.lock().unwrap().deref_mut() = state; let mut listeners = self.listeners.lock().unwrap(); for listener in listeners.iter_mut() { (listener.callback)(); @@ -130,43 +130,48 @@ impl Default for Writable where T: Default { } } -pub struct DerefGuard { +pub struct DerefGuard<'a, T> { listeners: Arc>>, tainted: bool, - inner: T + // this is an option because we need to drop the guard before we trigger the listeners + // this is only none during the drop function - see https://doc.rust-lang.org/stable/nomicon/destructors.html + inner: Option> } pub trait DerefGuardExt: WritableStore + Signal { - fn guard(&mut self) -> DerefGuard<&mut T>; + fn guard(&self) -> DerefGuard; } impl DerefGuardExt for Writable { - fn guard(&mut self) -> DerefGuard<&mut T> { + fn guard(&self) -> DerefGuard { DerefGuard { listeners: self.listeners.clone(), tainted: false, - inner: &mut self.state + inner: Some(self.state.lock().unwrap()) } } } -impl Deref for DerefGuard { - type Target = T; +impl<'a, T> Deref for DerefGuard<'a, T> { + type Target = MutexGuard<'a, T>; fn deref(&self) -> &Self::Target { - &self.inner + self.inner.as_ref().unwrap() } } -impl DerefMut for DerefGuard { +impl<'a, T> DerefMut for DerefGuard<'a, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.tainted = true; - &mut self.inner + self.inner.as_mut().unwrap() } } -impl Drop for DerefGuard { +impl<'a, T> Drop for DerefGuard<'a, T> { fn drop(&mut self) { if self.tainted { + let inner = self.inner.take().unwrap(); + drop(inner); + let mut listeners = self.listeners.lock().unwrap(); for listener in listeners.iter_mut() { (listener.callback)(); @@ -190,6 +195,11 @@ impl Signal for MutexGuard<'_, T> { self.deref().subscribe(callback) } } +impl Signal for Arc { + fn subscribe(&self, callback: Box) -> Box { + self.deref().subscribe(callback) + } +} impl Signal for Vec { /// Subscribes to all signals in the vector