wasm_snapshot_owner (wasm v0.3.0)
View SourceOne process per snapshot, holding what an Erlang term cannot hold for itself.
An image is a term and the BEAM will collect it, but a term cannot say "I am gone", and two things have to be given back when it is: the image's claim on its module, and its share of the node-wide byte budget. So each image gets a process whose life matches its own, and whose only job is to know who still wants it.
Why this is its own module
docs/architecture.md names three cycles and wasm_architecture_SUITE asserts
them, so joining one is a decision rather than a side effect. Holding a claim
means calling wasm_module_cache, and the cache calls wasm:compile/2 on a
miss, so anything long-lived that holds a claim and is reachable from the
facade is in that cycle. There is no arrangement that avoids it, only a
choice of which module joins.
This one does, and wasm_snapshot stays out. The mechanism -- what a capture
copies and what a restore lays over -- is then a module with no cycle in it,
and what joins the knot is fifty lines whose entire purpose is to hold a claim.
What it is not
Not the keeper. The plan put this in wasm_keeper, on the grounds that it is
already the long-lived owner of every other snapshot resource. The keeper keeps
its state in ETS rows that a restart adopts, so a second concern there is real
surgery on the one process a node cannot do without, and the guarantee does not
need it: what an image needs is a lifetime matching its own.
It also makes invalidation fall out rather than be enforced. The claim is made for this process, so it dies when this process does, and the cache is what sees to that.
Summary
Functions
Add a holder.
Charge an image, once, at capture.
Bytes currently charged to images across the node.
How many processes still hold this image, or gone.
Drop a holder. Never blocks, and has one result.
Start an owner holding Handle for FirstHolder, or say the module is gone.
Functions
Add a holder.
Holding a copied term is not ownership: the term crosses a message send for free, and an image whose holders are whoever happens to have a copy has no lifetime at all.
-spec charge(non_neg_integer()) -> ok | {error, wasm_error:error()}.
Charge an image, once, at capture.
A restore does not charge again: it takes the existing image, and the fresh memories it builds are an instance's and go to ordinary instance accounting where they belong. Charging per restore would make the budget mean something different depending on how many restores were in flight.
-spec charged() -> non_neg_integer().
Bytes currently charged to images across the node.
-spec holders(undefined | pid()) -> non_neg_integer() | gone.
How many processes still hold this image, or gone.
-spec refund(non_neg_integer()) -> integer().
Drop a holder. Never blocks, and has one result.
-spec start(wasm_module_cache:handle(), non_neg_integer(), pid()) -> {ok, pid()} | {error, not_loaded}.
Start an owner holding Handle for FirstHolder, or say the module is gone.
The claim is taken here rather than by the caller, because a claim belongs to the process that will give it back.