pub struct Component { /* private fields */ }Expand description
A node in the runtime component tree.
Components form a hierarchy: Session → Scenario → Phase → Dispenser. Each component carries its own labels, inheritable properties, and its own instrument registry.
Implementations§
Source§impl Component
impl Component
Sourcepub fn new(labels: Labels, props: HashMap<String, String>) -> Self
pub fn new(labels: Labels, props: HashMap<String, String>) -> Self
Create a new detached component with the given labels and props.
The component starts in ComponentState::Starting. Call
attach to wire it into the tree and compute effective labels.
Sourcepub fn root(labels: Labels, props: HashMap<String, String>) -> Arc<RwLock<Self>> ⓘ
pub fn root(labels: Labels, props: HashMap<String, String>) -> Arc<RwLock<Self>> ⓘ
Create a root component (session level). No parent.
Sourcepub fn effective_labels(&self) -> &Labels
pub fn effective_labels(&self) -> &Labels
Effective labels: all ancestor labels merged with own labels.
Sourcepub fn state(&self) -> ComponentState
pub fn state(&self) -> ComponentState
Current lifecycle state.
Sourcepub fn set_state(&mut self, state: ComponentState)
pub fn set_state(&mut self, state: ComponentState)
Transition to a new lifecycle state.
Reaching ComponentState::Stopped drops the liveness token, which is
what releases this component’s claim on its label set among its
siblings. Every stop path goes through here, so there is exactly one
place that can forget to do it.
Sourcepub fn register_instrument(
&mut self,
family: impl Into<String>,
instrument: InstrumentRef,
) -> Result<(), String>
pub fn register_instrument( &mut self, family: impl Into<String>, instrument: InstrumentRef, ) -> Result<(), String>
Register an instrument under family on this component.
Returns Err when family is already registered on this
component — duplicate-family declarations on the same
dimensional cell surface as a workload error here, before
any cycle runs (SRD-40b §7.2). The component’s
effective_labels define the dimensional cell; the same
family on a different component is a different cell and
produces no collision.
The collision check is a linear scan over the registry
Vec — see the storage-shape note on Self::instruments.
Sourcepub fn register_instrument_with_unit(
&mut self,
family: impl Into<String>,
unit: Option<String>,
instrument: InstrumentRef,
) -> Result<(), String>
pub fn register_instrument_with_unit( &mut self, family: impl Into<String>, unit: Option<String>, instrument: InstrumentRef, ) -> Result<(), String>
Variant of Self::register_instrument that records an
OpenMetrics unit (ms, bytes, …).
At capture time the unit drives the _<unit> suffix on
metric_family.name and populates the unit column per
SRD-40a §4.3 / SRD-40b §1. None is identical to the
no-unit register_instrument path.
Sourcepub fn instruments(&self) -> &[RegisteredInstrument]
pub fn instruments(&self) -> &[RegisteredInstrument]
Read-only view of every registered instrument on this component, in insertion order. Walked by the cadence reporter on every tick.
Sourcepub fn find_instrument(&self, family: &str) -> Option<&InstrumentRef>
pub fn find_instrument(&self, family: &str) -> Option<&InstrumentRef>
Linear scan by family name — diagnostic / rare-path only.
Hot-path callers must use the typed Arc<...> they
captured at registration time. The Vec storage and linear
scan are deliberate: registration is once-at-init,
per-cycle access is pre-bound, and a HashMap probe would
add API + Hash bound for ~40 ns saved once per workload load.
If you find yourself reaching for this on a per-cycle code
path, that’s a design bug in the caller — pre-bind the
Arc<...> you got from [register_instrument] instead.
Sourcepub fn set_dynamic_capture(&mut self, hook: Arc<dyn DynamicCapture>)
pub fn set_dynamic_capture(&mut self, hook: Arc<dyn DynamicCapture>)
Install a DynamicCapture hook for instruments whose
existence isn’t known at init time. Replaces any prior
installation. See the trait doc.
Sourcepub fn capture_delta(&self, interval: Duration) -> MetricSet
pub fn capture_delta(&self, interval: Duration) -> MetricSet
Capture a delta snapshot covering interval.
Drains histogram/timer reservoirs; counters report their
absolute running total (no draining). Called by the scheduler on
every tick — the result feeds the cadence reporter’s
smallest-cadence accumulator. The caller-supplied
interval is recorded on the snapshot verbatim; the
scheduler passes its nominal base_interval so the
“canonical scheduler cadence” property is preserved
in storage even when wall-clock between ticks drifts
(drift surfaces via the scheduler’s tick warning,
not by mutating the snapshot’s interval).
Also stamps last_capture_instant so the phase-end
flush path (capture_delta_auto) can compute true
elapsed time since the previous capture.
Sourcepub fn capture_delta_auto(&self, fallback: Duration) -> MetricSet
pub fn capture_delta_auto(&self, fallback: Duration) -> MetricSet
Phase-end variant of [capture_delta] that stamps the
snapshot with real elapsed wall time since the
previous capture, rather than a caller-supplied
nominal interval.
Eliminates the 1-second quantization that surfaced as
the spurious cycles_total_rate = 10000/8 = 1250.0
cluster for short phases — a 7.84-second phase will now
carry a 7843-ms interval (subject to storage precision)
instead of being padded to the scheduler’s nominal 1s
final-flush stamp.
fallback covers the edge case where no prior capture
has happened (a phase that ended before the first
scheduler tick). The phase-end caller passes the
scheduler’s nominal base_interval here — a phase
shorter than one tick still gets stamped with that
nominal duration rather than zero.
Sourcepub fn capture_current(&self) -> MetricSet
pub fn capture_current(&self) -> MetricSet
Capture a non-mutating snapshot of current state.
- Counters: absolute totals (atomic load).
- Gauges: current value.
- Histograms / Timers: non-draining clone (
peek_snapshot).
Never touches internal accumulators — callers may invoke this arbitrarily often without perturbing the scheduler’s per-tick cascade.
Sourcepub fn get_prop(&self, name: &str) -> Option<String>
pub fn get_prop(&self, name: &str) -> Option<String>
Get a property by name, walking up the tree.
Checks this component’s props first, then each ancestor in
order until found. Returns None if no ancestor has the key.
Sourcepub fn child_count(&self) -> usize
pub fn child_count(&self) -> usize
Number of child components.
Sourcepub fn children(&self) -> impl Iterator<Item = &Arc<RwLock<Component>>>
pub fn children(&self) -> impl Iterator<Item = &Arc<RwLock<Component>>>
Iterator over this component’s direct children.
Sourcepub fn controls(&self) -> &ControlRegistry
pub fn controls(&self) -> &ControlRegistry
Borrow this component’s dynamic-controls registry. Every component carries one; empty until something declares a control on it. See SRD 23.
Sourcepub fn cells(&self) -> Arc<CellMap> ⓘ
pub fn cells(&self) -> Arc<CellMap> ⓘ
This component’s data-materialised cells. See crate::cells::CellMap:
one series per dimension instance is one CHILD per instance, not a label
bag on an instrument.
Returns the Arc by value ON PURPOSE — see the field docs. Resolving
takes this component’s write lock, so the caller must be able to drop
its read guard first:
let cells = parent.read().unwrap().cells(); // guard dropped here
let cell = cells.resolve(&parent, &coord); // takes the write lockSourcepub fn find_control_up<T>(&self, name: &str) -> Option<Control<T>>
pub fn find_control_up<T>(&self, name: &str) -> Option<Control<T>>
Resolve a typed control by name, walking up the parent
chain. This component’s registry is checked first; then
each ancestor in order. An ancestor declaration is only
honored if its [BranchScope] is Subtree —
[BranchScope::Local] declarations do not propagate to
descendants. Returns None if no in-scope declaration
matches the <name, T> pair.
Mirrors Self::get_prop but for typed controls (SRD 23
§“Branch-scoped and final controls”).
Sourcepub fn find_control_erased_up(
&self,
name: &str,
) -> Option<Arc<dyn ErasedControl>>
pub fn find_control_erased_up( &self, name: &str, ) -> Option<Arc<dyn ErasedControl>>
Erased variant of Self::find_control_up — returns
just the enumeration handle, useful for diagnostics
(dryrun=controls, TUI surfaces) that don’t need the
typed value.
Sourcepub fn control_snapshot(
start: &Arc<RwLock<Component>>,
) -> HashMap<String, Arc<dyn ErasedControl>>
pub fn control_snapshot( start: &Arc<RwLock<Component>>, ) -> HashMap<String, Arc<dyn ErasedControl>>
SRD-89 — flatten the up-walk control resolution starting at start
into a name → erased-handle map, computed without nested locks.
This has the same visibility as calling
Self::find_control_erased_up for every name — the start
component’s own controls plus any BranchScope::Subtree control on an
ancestor, nearest-wins — but it acquires and releases one tier’s
lock at a time (never holding a child’s guard while reading a parent),
so it is immune to the writer-preferring RwLock starvation that a
nested up-walk hits under concurrent in-process execution (a hot-path
per-cycle find_control_erased_up deadlocks against the cadence
path’s instrument-registration writes; this is built once per phase
and read lock-free thereafter — see SRD-89 §3c-i).
Sourcepub fn running_descendant_count(&self) -> usize
pub fn running_descendant_count(&self) -> usize
Count of Running-state descendants (this component’s
children, grandchildren, …). Used by callers that want a
structural “how many phases are in flight?” query against
the live component tree — e.g. the TUI’s Focus-LOD
placeholder decision (SRD 62 §“Scenario done?”).
The component itself is NOT counted — the query is meant to traverse from an activity root into its phases.