pub struct Budget { /* private fields */ }Expand description
Tracks one run against its Limits.
A Budget is not Clone: it is one run’s, and a second one would be a
second run. What is shared instead is Meter, the view of the same
accounting that a safepoint charges through, and every task thread of the
run holds one — ADR 0008 draws a task’s fuel from the run’s budget rather
than giving each task one of its own, so there is still exactly one
authoritative count of what the run spent. Share a Cancellation when
another thread needs to stop the run.
max_call_depth is the one limit a budget does not itself enforce. Call
depth is a property of one stack, and with a thread per task there is a
stack per task, so the interpreter checks its own depth against
Limits::max_call_depth; counting every task’s frames into one number
would stop a shallow task because a sibling was deep.
Implementations§
Source§impl Budget
impl Budget
Sourcepub fn new(limits: Limits) -> Self
pub fn new(limits: Limits) -> Self
Tracks a run against limits, starting the deadline clock now.
Sourcepub fn with_cancellation(limits: Limits, cancellation: Cancellation) -> Self
pub fn with_cancellation(limits: Limits, cancellation: Cancellation) -> Self
Tracks a run against limits, using a Cancellation the caller
already holds a handle to, so it can be cancelled from elsewhere.
Sourcepub fn meter(&self) -> Meter
pub fn meter(&self) -> Meter
This run’s accounting, in the handle a safepoint charges through.
A caller that will charge more than once holds on to what this
answers: taking one costs an Arc clone, and charging through one
costs no lock at all. Meter says where each backend takes its own
and why that is where a run begins.
Sourcepub fn cancellation(&self) -> Cancellation
pub fn cancellation(&self) -> Cancellation
The cancellation flag for this run. Clone and hand it to whoever may need to cancel the run from another thread.
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. The
interpreter calls this at safepoints: loop back edges, calls, and
await. fuel is the cost of the work performed since the last
safepoint.
Meter::safepoint is the whole of it. A backend on a per-instruction
path holds a Meter and calls that instead of reaching a Budget
through the registry’s lock; this is here for a caller that has a
Budget in hand and charges once.
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. Meter::spend says when that is what a backend wants.
Sourcepub fn charge_host_call(&self) -> Result<(), Stopped>
pub fn charge_host_call(&self) -> Result<(), Stopped>
Charges one host call against the budget, failing before the call is
dispatched if the run was cancelled, if its deadline has passed, or if
the call would exceed max_host_calls.
A host call is a control point exactly as a safepoint is. ADR 0003
puts the controls at “loop back edges, calls, and await”, and a run
whose work is waiting on a host reaches none of the other three: a
deadline checked only in Cove code would not bound a program that
spends its time inside calls. The clock is read on every call rather
than every DEADLINE_CHECK_INTERVALth, because a host call already
costs far more than reading it does.
Sourcepub fn charge_task(&self) -> Result<(), Stopped>
pub fn charge_task(&self) -> Result<(), Stopped>
Charges one task against the concurrency limit, refusing it before it is given a thread if the run already holds as many tasks as it may.
Every other limit stops a run for work it has already done. This one
refuses work that has not started, because a thread is taken rather
than spent: by the time a safepoint could observe it, the resource is
already held. A refusal stops the run the way exhausted fuel does; a
spawn that waited for a sibling to finish would be a scheduler, and
ADR 0008 deliberately has no scheduling policy.
The check and the taking are one step, so two spawns racing for the
last place cannot both be told there is one. That used to be the
registry’s mutex; it is this compare-and-swap now, which holds however
this is reached.
Sourcepub fn release_task(&self)
pub fn release_task(&self)
Forgets a task whose end has been observed, so its place is free again.
A task ends by finishing, by failing, by being cancelled, or by breaking an invariant in its own thread, and all four reach the caller as a join. Releasing anywhere else would make this a limit on how many tasks a run may spawn in total rather than on how many it may hold at once.
Sourcepub fn live_tasks(&self) -> u64
pub fn live_tasks(&self) -> u64
How many spawned tasks are alive right now: what the concurrency limit bounds, and what a stop reports.
Sourcepub fn fuel_spent(&self) -> u64
pub fn fuel_spent(&self) -> u64
Total fuel spent so far, for reporting.
Sourcepub fn host_calls(&self) -> u64
pub fn host_calls(&self) -> u64
Total host calls charged so far, including any that were then rejected for exceeding the limit, 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.