euv_core/reactive/profiler/struct.rs
1//! Profiler data types.
2//!
3//! Data containers for the profiler (`ProfileEntry`, `ProfilerHandle`,
4//! `ProfilerMark`).
5use super::*;
6
7/// A single recorded measurement.
8///
9/// Pushed onto the `ProfilerHandle`'s entries signal every time
10/// the user calls `ProfilerHandle::measure(label, f)` (or
11/// manually `begin` / `end`). Cheap to `Clone` (the inner
12/// strings are small and `f64` is `Copy`).
13///
14/// Field-level semantics:
15///
16/// - `label`: free-form identifier — typically the call site name
17/// (`"render-list"`, `"fetch-posts"`). Empty strings are
18/// allowed but render as an empty chip in the UI, which makes
19/// misconfigured measurements obvious in a profiler readout.
20/// - `elapsed_ms`: wall-clock time between `begin()` and the
21/// matching `end()` (or the duration of the measured closure),
22/// in milliseconds. Always `>= 0.0` — `begin` is captured
23/// before any user code runs, so the subtraction cannot
24/// underflow.
25/// - `timestamp_ms`: the wall-clock `now_ms()` value at the
26/// instant the entry was recorded (NOT the start of the
27/// measurement). This lets the UI sort / filter entries by
28/// when they were committed, not by when the user started
29/// the timer — which matters when entries are kept around
30/// for "last N measurements" readouts.
31#[derive(Clone, Debug, PartialEq, Data, New)]
32pub struct ProfileEntry {
33 /// The free-form label passed to `measure` / `begin`.
34 pub label: String,
35 /// Duration of the measured operation, in milliseconds.
36 #[get(type(copy))]
37 pub elapsed_ms: f64,
38 /// Wall-clock time at which the entry was recorded.
39 #[get(type(copy))]
40 pub timestamp_ms: f64,
41}
42
43/// A handle to the profiler registered against the current
44/// hook context.
45///
46/// The handle owns the entries signal; calling
47/// `ProfilerHandle::entries()` returns that signal so any
48/// reactive read (`Signal::get()`) inside a closure subscribes
49/// the enclosing render to new entries. The matching
50/// measurement API is `ProfilerHandle::measure(label, f)`
51/// (push-on-exit) or `ProfilerHandle::begin(label)` /
52/// `ProfilerHandle::end()` (split-timer API for code paths
53/// that don't fit inside a single closure).
54///
55/// # Lifecycle
56///
57/// The handle is obtained via `App::use_profiler()` (or
58/// directly via `HookContext::profiler()`), which slots it into
59/// the current hook context. On every render at the same hook
60/// index, the same handle is returned — so measurements
61/// recorded from a previous render remain visible in
62/// `entries()`.
63///
64/// On hook-context teardown (component unmount, match-arm
65/// switch, or explicit `clear()`), the handle is dropped and
66/// its entries signal goes with it. If you need to keep
67/// measurements alive past the lifetime of the component,
68/// clone the entries vector out before the context is cleared.
69#[derive(Clone, Data, New)]
70pub struct ProfilerHandle {
71 /// The reactive log of measurements. Every measurement
72 /// pushes a fresh `ProfileEntry` into this vector via
73 /// `.set(...)` — the `set` triggers the reactive update
74 /// path, so any subscriber re-renders.
75 pub(crate) entries: Signal<Vec<ProfileEntry>>,
76}
77
78/// A `begin()` marker — RAII guard that records the start
79/// timestamp and the label so the matching `end()` call can
80/// compute the elapsed time.
81///
82/// Created by `ProfilerHandle::begin(label)`. Consume with
83/// `end()` to push a `ProfileEntry` into the entries signal.
84/// Dropping the marker without calling `end()` discards the
85/// measurement silently (we don't have a place to push a
86/// half-finished entry, and panicking on drop is hostile).
87#[derive(Data, New)]
88pub struct ProfilerMark {
89 /// The label this marker was created with. Copied out of
90 /// the `&str` at construction time so the marker does not
91 /// outlive any borrowed string.
92 pub(crate) label: String,
93 /// Wall-clock timestamp captured at `begin()`. Subtracted
94 /// from the `end()` timestamp to compute `elapsed_ms`.
95 pub(crate) started_ms: f64,
96 /// Back-reference to the entries signal. Cloned (cheap —
97 /// `Signal<T>` is `Copy`-by-pointer) so `end()` can push
98 /// without re-borrowing the handle.
99 pub(crate) entries: Signal<Vec<ProfileEntry>>,
100}