pub struct AnalysisBudget { /* private fields */ }Expand description
The run’s budget for host-side type-provider work.
§Why this is a second clock rather than a share of RunClock
RunClock bounds guest execution and is polled from inside a handler by both engines.
A tsc provider’s cost is neither: it is the user’s own TypeScript program being built,
in a process lanekeep spawned, while no rule is running. Charging it to the run budget
would make a cold provider run and a warm one take different exits over identical input,
which is the determinism argument architecture §6.8 already makes for taking component
compilation off the run clock.
§Why an accumulator rather than a wall clock
This budget is analysis time, not elapsed time. An Instant taken at prepare charges
discovery, hashing, parsing, matching and every rule that ran to a budget whose own
breach message says it is “the cost of building the project’s own TypeScript program and
not of running any rule” — so the message would be a lie, and a large corpus whose program
build takes half a minute would be cancelled for spending the rest of the minute doing the
work the run exists to do. Only what AnalysisBudget::charge brackets is charged.
It is the mirror image of Budget::pause in intent: the rule clock stops where the
analysis clock runs, so no instant is charged to both.
The converse does not hold, and that is deliberate. What a provider charges is service
time — the window in which its sidecar is working on one request — so an instant a worker
spends queued behind another worker’s request is charged to neither clock. Charging the
queue wait instead would make the accumulator grow with the number of rayon workers rather
than with the work: measured through the tsc provider, fourteen workers each waiting
about 200 ms charged 2.866 s against 205 ms of wall clock, so a 60 s budget bounded roughly
60/P seconds of real analysis and the breach message quoted a duration nobody could
observe. One sidecar serves the run, so the sum of its service times is the wall time it
was busy — which is exactly what “type analysis took …” claims to name.
Clone over a shared accumulator rather than Copy over an Instant: the engine holds
one and the provider holds another, and a charge on either has to be visible to the check
the other makes. Two clones are the same budget, not two budgets.
Implementations§
Source§impl AnalysisBudget
impl AnalysisBudget
Sourcepub fn charge(&self) -> Charge<'_>
pub fn charge(&self) -> Charge<'_>
Charge everything until the returned guard drops to this budget.
A guard rather than a matched pair for Paused’s reason: the call it brackets can
return early on ?, and a stop whose start sits after a ? charges nothing for the
one request that actually ran long.
Nesting composes by over-charging rather than by being forbidden — an inner charge’s time lands in the accumulator twice — so the caller brackets the outermost call it owns and nothing inside it.
Bracket the service, never the wait for it. A provider whose sidecar serves one request at a time must take this guard after it holds whatever serializes access, or every worker queued behind the one being served charges the queue and the accumulator counts the same instants once per waiting thread. See the type’s own documentation.
Sourcepub fn remaining(&self) -> Option<Duration>
pub fn remaining(&self) -> Option<Duration>
How much of it is left, or None once it is breached.
Some(Duration::ZERO) is a real answer and is not the same as None: a request handed
a zero I/O timeout fails immediately and reports as a timeout, where None means the
run is already over and nothing further should be attempted. The boundary is the same
one analysis_overrun uses, so the two can never disagree about a single instant.
Trait Implementations§
Source§impl Clone for AnalysisBudget
impl Clone for AnalysisBudget
Source§fn clone(&self) -> AnalysisBudget
fn clone(&self) -> AnalysisBudget
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more