Start work on instrumenting `leptos_reactive`

This commit is contained in:
Greg Johnston 2023-01-06 09:49:39 -05:00
parent b010233bb4
commit 96418ed684
8 changed files with 347 additions and 9 deletions

View File

@ -6,8 +6,10 @@ edition = "2021"
[dependencies]
console_error_panic_hook = "0.1"
gloo = { version = "0.8", features = ["futures"] }
leptos = { path = "../../../leptos" }
leptos = { path = "../../../leptos", features = ["tracing"] }
tracing = "0.1"
tracing-subscriber = "0.3"
wasm-bindgen-futures = "0.4"
web-sys = "0.3"
web-sys = "0.3"
[workspace]

View File

@ -6,6 +6,7 @@ extern crate tracing;
mod utils;
use leptos::*;
use tracing::field::debug;
use tracing_subscriber::util::SubscriberInitExt;
fn main() {
@ -58,6 +59,7 @@ fn view_fn(cx: Scope) -> impl IntoView {
<div>
<button on:click=handle_toggle>"Toggle"</button>
</div>
<Example/>
<A child=Signal::from(a) />
<A child=Signal::from(b) />
</>
@ -71,7 +73,20 @@ fn A(cx: Scope, child: Signal<View>) -> impl IntoView {
#[component]
fn Example(cx: Scope) -> impl IntoView {
view! { cx,
trace!("rendering <Example/>");
let (value, set_value) = create_signal(cx, 10);
let memo = create_memo(cx, move |_| value() * 2);
create_effect(cx, move |_| {
trace!("logging value of memo..., {}", memo.get());
});
set_timeout(move || { set_value.update(|v| *v += 1)}, std::time::Duration::from_millis(50));
view! { cx,
<h1>"Example"</h1>
}
}

View File

@ -20,6 +20,7 @@ serde_json = "1"
base64 = "0.13"
thiserror = "1"
tokio = { version = "1", features = ["rt"], optional = true }
tracing = "0.1"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

View File

@ -47,13 +47,26 @@ use std::fmt::Debug;
/// # }
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
scope = %format!("{:?}", cx.id),
ty = %std::any::type_name::<T>()
)
)
)]
#[track_caller]
pub fn create_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
where
T: 'static,
{
cfg_if! {
if #[cfg(not(feature = "ssr"))] {
create_isomorphic_effect(cx, f);
let e = cx.runtime.create_effect(f);
cx.with_scope_property(|prop| prop.push(ScopeProperty::Effect(e)))
} else {
// clear warnings
_ = cx;
@ -88,6 +101,18 @@ where
/// });
/// # assert_eq!(b(), 2);
/// # }).dispose();
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
scope = %format!("{:?}", cx.id),
ty = %std::any::type_name::<T>()
)
)
)]
#[track_caller]
pub fn create_isomorphic_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
where
T: 'static,
@ -97,6 +122,17 @@ where
}
#[doc(hidden)]
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
scope = %format!("{:?}", cx.id),
ty = %std::any::type_name::<T>()
)
)
)]
pub fn create_render_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
where
T: 'static,
@ -116,6 +152,8 @@ where
{
pub(crate) f: F,
pub(crate) value: RefCell<Option<T>>,
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static std::panic::Location<'static>
}
pub(crate) trait AnyEffect {
@ -127,6 +165,18 @@ where
T: 'static,
F: Fn(Option<T>) -> T,
{
#[cfg_attr(
debug_assertions,
instrument(
name = "Effect::run()",
level = "debug",
skip_all,
fields(
id = %format!("{:?}", id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
fn run(&self, id: EffectId, runtime: RuntimeId) {
with_runtime(runtime, |runtime| {
// clear previous dependencies
@ -162,6 +212,17 @@ impl EffectId {
})
}
#[cfg_attr(
debug_assertions,
instrument(
name = "Effect::cleanup()",
level = "debug",
skip_all,
fields(
id = %format!("{:?}", self),
)
)
)]
pub(crate) fn cleanup(&self, runtime: &Runtime) {
let sources = runtime.effect_sources.borrow();
if let Some(sources) = sources.get(*self) {

View File

@ -66,11 +66,13 @@
//! });
//! ```
#[cfg_attr(debug_assertions, macro_use)]
pub extern crate tracing;
mod context;
mod effect;
mod hydration;
mod memo;
mod resource;
mod runtime;
mod scope;

View File

@ -115,7 +115,11 @@ where
/// # }).dispose();
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct Memo<T>(pub(crate) ReadSignal<Option<T>>)
pub struct Memo<T>(
pub(crate) ReadSignal<Option<T>>,
#[cfg(debug_assertions)]
pub(crate) &'static std::panic::Location<'static>
)
where
T: 'static;
@ -124,13 +128,28 @@ where
T: 'static,
{
fn clone(&self) -> Self {
Self(self.0)
Self(
self.0,
#[cfg(debug_assertions)]
self.1
)
}
}
impl<T> Copy for Memo<T> {}
impl<T> UntrackedGettableSignal<T> for Memo<T> {
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.0.id),
defined_at = %format!("{:?}", self.1)
)
)
)]
fn get_untracked(&self) -> T
where
T: Clone,
@ -140,6 +159,17 @@ impl<T> UntrackedGettableSignal<T> for Memo<T> {
self.0.get_untracked().unwrap()
}
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.0.id),
defined_at = %format!("{:?}", self.1)
)
)
)]
fn with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> O {
// Unwrapping here is fine for the same reasons as <Memo as
// UntrackedSignal>::get_untracked
@ -167,6 +197,18 @@ where
/// # }).dispose();
/// #
/// ```
#[cfg_attr(
debug_assertions,
instrument(
name = "Memo::get()",
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.0.id),
defined_at = %format!("{:?}", self.1)
)
)
)]
pub fn get(&self) -> T
where
T: Clone,
@ -194,6 +236,18 @@ where
/// # }).dispose();
/// #
/// ```
#[cfg_attr(
debug_assertions,
instrument(
name = "Memo::with()",
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.0.id),
defined_at = %format!("{:?}", self.1)
)
)
)]
pub fn with<U>(&self, f: impl FnOnce(&T) -> U) -> U {
// okay to unwrap here, because the value will *always* have initially
// been set by the effect, synchronously

View File

@ -112,6 +112,7 @@ impl RuntimeId {
ret
}
#[track_caller]
pub(crate) fn create_signal<T>(self, value: T) -> (ReadSignal<T>, WriteSignal<T>)
where
T: Any + 'static,
@ -127,11 +128,15 @@ impl RuntimeId {
runtime: self,
id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
},
WriteSignal {
runtime: self,
id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
},
)
}
@ -150,17 +155,25 @@ impl RuntimeId {
runtime: self,
id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
}
}
#[track_caller]
pub(crate) fn create_effect<T>(self, f: impl Fn(Option<T>) -> T + 'static) -> EffectId
where
T: Any + 'static,
{
#[cfg(debug_assertions)]
let defined_at = std::panic::Location::caller();
with_runtime(self, |runtime| {
let effect = Effect {
f,
value: RefCell::new(None),
#[cfg(debug_assertions)]
defined_at
};
let id = { runtime.effects.borrow_mut().insert(Rc::new(effect)) };
id.run::<T>(self);
@ -168,10 +181,14 @@ impl RuntimeId {
})
}
#[track_caller]
pub(crate) fn create_memo<T>(self, f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
where
T: PartialEq + Any + 'static,
{
#[cfg(debug_assertions)]
let defined_at = std::panic::Location::caller();
let (read, write) = self.create_signal(None);
self.create_effect(move |_| {
@ -186,7 +203,11 @@ impl RuntimeId {
}
});
Memo(read)
Memo(
read,
#[cfg(debug_assertions)]
defined_at
)
}
}

View File

@ -45,6 +45,18 @@ use thiserror::Error;
/// # }).dispose();
/// #
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
scope = %format!("{:?}", cx.id),
ty = %std::any::type_name::<T>()
)
)
)]
#[track_caller]
pub fn create_signal<T>(cx: Scope, value: T) -> (ReadSignal<T>, WriteSignal<T>) {
let s = cx.runtime.create_signal(value);
cx.with_scope_property(|prop| prop.push(ScopeProperty::Signal(s.0.id)));
@ -54,6 +66,16 @@ pub fn create_signal<T>(cx: Scope, value: T) -> (ReadSignal<T>, WriteSignal<T>)
/// Creates a signal that always contains the most recent value emitted by a [Stream].
/// If the stream has not yet emitted a value since the signal was created, the signal's
/// value will be `None`.
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
scope = %format!("{:?}", cx.id),
)
)
)]
pub fn create_signal_from_stream<T>(
cx: Scope,
mut stream: impl Stream<Item = T> + Unpin + 'static,
@ -120,6 +142,8 @@ where
pub(crate) runtime: RuntimeId,
pub(crate) id: SignalId,
pub(crate) ty: PhantomData<T>,
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static std::panic::Location<'static>
}
impl<T> UntrackedGettableSignal<T> for ReadSignal<T> {
@ -157,6 +181,14 @@ where
/// assert_eq!(first_char(), 'B');
/// });
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(signal = %format!("{:?}", self.id))
)
)]
pub fn with<U>(&self, f: impl FnOnce(&T) -> U) -> U {
self.id.with(self.runtime, f)
}
@ -184,6 +216,14 @@ where
/// assert_eq!(count(), 0);
/// });
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(signal = %format!("{:?}", self.id))
)
)]
pub fn get(&self) -> T
where
T: Clone,
@ -219,6 +259,8 @@ impl<T> Clone for ReadSignal<T> {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: self.defined_at
}
}
}
@ -298,6 +340,8 @@ where
pub(crate) runtime: RuntimeId,
pub(crate) id: SignalId,
pub(crate) ty: PhantomData<T>,
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static std::panic::Location<'static>
}
impl<T> UntrackedSettableSignal<T> for WriteSignal<T>
@ -342,6 +386,18 @@ where
/// assert_eq!(count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
name = "WriteSignal::update()",
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn update(&self, f: impl FnOnce(&mut T)) {
self.id.update(self.runtime, f);
}
@ -367,6 +423,17 @@ where
/// assert_eq!(count(), 2);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn update_returning<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
self.id.update(self.runtime, f)
}
@ -390,6 +457,17 @@ where
/// assert_eq!(count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn set(&self, new_value: T) {
self.id.update(self.runtime, |n| *n = new_value);
}
@ -401,6 +479,8 @@ impl<T> Clone for WriteSignal<T> {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: self.defined_at
}
}
}
@ -460,6 +540,16 @@ where
/// # }).dispose();
/// #
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
ty = %std::any::type_name::<T>()
)
)
)]
pub fn create_rw_signal<T>(cx: Scope, value: T) -> RwSignal<T> {
let s = cx.runtime.create_rw_signal(value);
cx.with_scope_property(|prop| prop.push(ScopeProperty::Signal(s.id)));
@ -495,6 +585,8 @@ where
pub(crate) runtime: RuntimeId,
pub(crate) id: SignalId,
pub(crate) ty: PhantomData<T>,
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static std::panic::Location<'static>
}
impl<T> Clone for RwSignal<T> {
@ -503,6 +595,8 @@ impl<T> Clone for RwSignal<T> {
runtime: self.runtime,
id: self.id,
ty: self.ty,
#[cfg(debug_assertions)]
defined_at: self.defined_at
}
}
}
@ -578,7 +672,18 @@ where
/// assert_eq!(count(), 0);
/// # }).dispose();
/// #
/// ```
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn get(&self) -> T
where
T: Clone,
@ -603,6 +708,17 @@ where
/// assert_eq!(count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn update(&self, f: impl FnOnce(&mut T)) {
self.id.update(self.runtime, f);
}
@ -626,6 +742,17 @@ where
/// assert_eq!(count(), 2);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn update_returning<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
self.id.update(self.runtime, f)
}
@ -644,6 +771,17 @@ where
/// assert_eq!(count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
pub fn set(&self, value: T) {
self.id.update(self.runtime, |n| *n = value);
}
@ -664,11 +802,25 @@ where
/// assert_eq!(read_count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
#[track_caller]
pub fn read_only(&self) -> ReadSignal<T> {
ReadSignal {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
}
}
@ -686,11 +838,25 @@ where
/// assert_eq!(count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
#[track_caller]
pub fn write_only(&self) -> WriteSignal<T> {
WriteSignal {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
}
}
@ -707,17 +873,33 @@ where
/// assert_eq!(get_count(), 1);
/// # }).dispose();
/// ```
#[cfg_attr(
debug_assertions,
instrument(
level = "trace",
skip_all,
fields(
id = %format!("{:?}", self.id),
defined_at = %format!("{:?}", self.defined_at)
)
)
)]
#[track_caller]
pub fn split(&self) -> (ReadSignal<T>, WriteSignal<T>) {
(
ReadSignal {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
},
WriteSignal {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller()
},
)
}