Heaps and Heap References
Heaps
In Starlark, there are three interesting heap-related points of interest:
- A
HeaphasValue's allocated on it and cannot be cloned or shared. - A
FrozenHeaphasFrozenValue's allocated on it and cannot be cloned or shared. - A
FrozenHeapRefis aFrozenHeapthat is now read-only and can now be cloned and shared.
A FrozenHeapRef keeps a heap alive. While you have a FrozenValue, it is
important that you have either the FrozenHeap itself, or more usually, a
FrozenHeapRef to it. A FrozenHeap may contains a set of FrozenHeapRef's to
keep the FrozenHeaps it references alive.
Heap Containers
Heaps are included in other data types:
- A
Modulecontains aHeap(where normal values are allocated) and aFrozenHeap(stores references to other frozen heaps and has compilation constants allocated on it). TheHeapportion is garbage collected. At the end, when you callfreeze,Value's referenced by name in theModuleare moved to theFrozenHeapand then thenFrozenHeapis sealed to produce aFrozenHeapRef. - A
FrozenModulecontains aFrozenHeapRef. - A
GlobalsBuildercontains aFrozenHeaponto which values are allocated. - A
Globalscontains aFrozenHeapRef.
Heap References
It is important that when a FrozenValue X is referenced by a Value or
FrozenValue (for example, included in a list), the heap where X originates is
added as a reference to the heap where the new value is being created.
As a concrete example in pseudo-code:
let h1 = FrozenHeap::new();
let s = "test".alloc(h1);
let h1 : FrozenHeapRef = h1.into_ref();
let h2 = Heap::new();
h2.add_reference(h1);
vec![s].alloc(h2);
In the above code, the following steps are taken:
- Create a
FrozenHeapthen allocate something in it. - Turn the heap into a reference.
- Use the allocated value
sfromh1when constructing a value inh2. - For that to be legal, and for the heap
h1to not disappear while it is being allocated, it is important to calladd_reference.
Note that this API can only point at a FrozenValue from another heap, and only
after that heap has been turned into a reference, so it will not be allocated in
anymore. These restrictions are deliberate and mean that most programs only have
one 'active heap' at a time.
Following are some places where heap references are added by Starlark:
- Before evaluation is started, a reference is added to the
Globalsfrom theModule, so it can access the global functions. - When evaluating a
loadstatement, a reference is added to theFrozenModulethat is being loaded. - When freezing a module, the
FrozenHeap, in theModule, is moved to theFrozenModule, preserving the references that were added.
OwnedFrozenValue
When you get a value from a FrozenModule, it will be a OwnedFrozenValue.
This structure is a pair of a FrozenHeapRef and a FrozenValue, where the ref
keeps the value alive. You can move that OwnedFrozenValue into the value of a
module with code such as:
fn move<'v>(from: &FrozenModule, to: &'v Module) {
let x : OwnedFrozenValue = from.get("value").unwrap();
let v : Value<'v> = x.owned_value(&to);
to.set("value", v);
}
In general, you can use the OwnedFrozenValue in one of three ways:
- Operate on it directly - with methods like
unpack_i32orto_str. - Extract it safely - using methods like
owned_frozen_value, which takes aFrozenHeapto which the heap reference is added and returns a nakedFrozenValue. After that, it is then safe for theFrozenHeapyou passed in to use theFrozenValue.- With
owned_value, there is lifetime checking that the right heap is passed, but withFrozenValue, there isn't. - Be careful to pass the right heap, although given most programs only have one active heap at a time, it should mostly work out.
- With
- Extract it unsafely - using methods
unchecked_frozen_value, which gives you the underlyingFrozenValuewithout adding any references.- Be careful to make sure there is a good reason the
FrozenValueremains valid.
- Be careful to make sure there is a good reason the