pub struct Meter { /* private fields */ }Expand description
One run’s budget as a safepoint charges it: a handle every task thread can hold at once, over counters that need no lock.
§Why this is not a &mut Budget
It used to be. crate::host::HostRegistry::with_budget locked a mutex,
handed the closure a &mut Budget, and unlocked — at every call and at
every return, because every call and every return is a safepoint. Issue
#182 measured what that cost: on benches/call, with_budget plus
pthread_mutex_lock plus pthread_mutex_unlock were 36% of the run
against the predecessor’s execute at 46%.
The lock was not protecting anything that needed one. A safepoint adds to
fuel_spent, reads an atomic flag, compares against a limit fixed before
the run, and every so often reads a clock that started before the run.
None of that is a multi-field invariant two threads could tear; the
counters were plain integers because the struct holding them happened to
be reached by &mut, not because anything wanted them to be. So they are
atomics, this is the &self view of them, and the mutex is left to what
installs a budget and what reads the counters back.
§What is still the mutex’s
crate::host::HostRegistry::with_budget still exists and still locks. It
is how a budget is installed, how cove run --stats reads what a run
spent, and how the charges that are not per-instruction are made — a host
call, a spawn, a task that ended. Every one of those is bounded by
something far more expensive than a lock, and moving them would have been
churn without a number behind it.
§Taking one, and restarts
A Meter names the accounting of the run it was taken from rather than
“whatever budget the registry holds now”. Budget::restart gives its
budget fresh accounting, so a Meter taken before a restart charges the
run that ended. Both backends therefore take theirs where a run begins:
Vm::new and Interpreter::new take one, and invoke_within and
run_entry_within take another immediately after installing the budget
they were handed. A registry’s budget cannot be replaced by any other
route — set_budget needs &mut HostRegistry and a backend holds the
registry by shared reference for as long as it exists — so those are all
the places a stale one could come from.
Implementations§
Source§impl Meter
impl Meter
Sourcepub fn limits(&self) -> &Limits
pub fn limits(&self) -> &Limits
The limits the run was given, which do not change while it lasts.
Sourcepub fn is_cancelled(&self) -> bool
pub fn is_cancelled(&self) -> bool
Whether the run has been cancelled from outside.
The run’s flag, which every task of it shares. A task’s own flag and
a bounded call’s belong to one thread, and crate::interp::stopped_here
is where those two are read.
Sourcepub fn safepoint(&self, fuel: u64) -> Result<(), Stopped>
pub fn safepoint(&self, fuel: u64) -> Result<(), Stopped>
Checks cancellation, the deadline, and fuel in one call. Both backends
call this at their safepoints. fuel is the cost of the work performed
since the last one.
The order the three questions are asked in is the whole of what a caller can observe about this, and it is the order they were asked in when a mutex was held across all three.
Sourcepub fn spend(&self, fuel: u64)
pub fn spend(&self, fuel: u64)
Adds fuel to the run’s total without asking whether the run may
continue.
A backend that charges fuel in batches holds some between two
safepoints, and the safepoint is where that holding is spent. A run
that ends anywhere else — by raising, by being stopped, by a task
thread finishing — reaches no further safepoint, so what it had
gathered would simply not be counted, and fuel_spent would report
less work than the run did. This is where the last of it is put back,
and it decides nothing: the run is already over, and a second stop
raised here would be answering a question nobody asked.
Sourcepub fn fuel_spent(&self) -> u64
pub fn fuel_spent(&self) -> u64
Total fuel spent so far, for reporting.
Sourcepub fn to_runtime_error(&self, stopped: Stopped) -> RuntimeError
pub fn to_runtime_error(&self, stopped: Stopped) -> RuntimeError
Converts why execution stopped into a RuntimeError naming the
limit and its configured value, quoting ADR 0001’s position that these
are runtime controls rather than termination proofs.