single: root; introduction
Roots
tell the garbage collector
where to start tracing <trace>
. The garbage collector determines which blocks are reachable
from the roots, and (in automatically
managed <automatic memory management>
pools
) reclaims the unreachable
blocks. This is quite efficient and can be a very good approximation to liveness <live>
.
It is therefore important that all references
that the client program
can directly access are registered as roots, otherwise the garbage collector might recycle an object that would be used in the future. Some collectors, for example Boehm's, assume that all references stored in static data are roots; the Memory Pool System is more flexible, but requires the client program to declare which references are roots.
single: root; registering
You can register a root at any time by calling one of the mps_root_create
functions. Roots may not be registered twice, and no two roots may overlap (that is, each reference is fixed
by at most one root). Roots may be:
- in
registers
; - on the program's
control stack
; - in the program's static data;
- in
heap
not managed by the MPS (provided that you destroy the root before freeing it; seethe Scheme interpreter's global symbol table <guide-lang-roots-rehash>
for an example); - in
manually managed <manual memory management>
pools (provided that you remove the root before freeing it).
Roots must not be in memory that is subject to garbage
collection
(and so roots must not be in automatically managed
<automatic memory management>
pools).
When you register a root you describe to the MPS how to scan
it for references, providing your own scanning function in the cases of :cmps_root_create
and :cmps_root_create_fmt
. Such a root scanning function must follow the topic-scanning-protocol
.
All the references in a root are of the same rank
(just as in a formatted object
). So they are all exact <exact
reference>
, ambiguous <ambiguous reference>
or weak
<weak reference (1)>
.
Note
If the rank of the root is exact <exact reference>
, or weak <weak reference (1)>
, the references in the root must always be valid while the root is registered: that is, they must be references to actual objects or null pointers. This could be immediately after the root is registered, so the root must be valid before it is registered.
Note
As with scanning <topic-scanning>
in general, it's safe to fix
references that point to memory not managed by the MPS. These will be ignored.
Roots can be deregistered at any time by calling :cmps_root_destroy
. All roots registered in an arena
must be deregistered before the arena is destroyed.
There are four ways to register a root, depending on how you need to scan it for references:
- :c
mps_root_create
if you need a custom root scanning function (of type :cmps_root_scan_t
); - :c
mps_root_create_fmt
if the root consists of a block of objects belonging to anobject format
, which can be scanned by the format'sscan method
(of type :cmps_fmt_scan_t
); - :c
mps_root_create_area
if the root consists of an area of memory; - :c
mps_root_create_thread
if the root consists of theregisters
andcontrol stack
of a thread. Seetopic-root-thread
below.
Several of these categories of roots have variants for dealing with tagged references
. See topic-scanning-tag
.
pair: root; cautions
Creating a root and then registering is similar to reserving a block and then committing it (in the topic-allocation-point-protocol
), and similar cautions
<topic-allocation-cautions>
apply. Before registering a root:
- The root must be valid (that is, the appropriate root scanning function can scan it).
- All
exact references
in the root (references that arefixed
by the root scanning function) must contain valid references or null pointers. - You must not store a reference in the root to a block in an automatically managed pool (such a reference is hidden from the MPS until you register the root, and may become invalid).
So the typical sequence of operations when creating a root is:
- Initialize references in the root with null pointers or other safe values.
- Register the root.
- Fill in the references in the root.
pair: root; thread
Every thread's registers
and control stack
potentially contain references to allocated objects, so should be registered as a root by calling :cmps_root_create_thread
.
The MPS's stack scanner needs to know how to find the cold end
of the part of the stack to scan. The cold end
of the relevant part of the stack can be found by taking the address of a local variable in the function that calls the main work function of your thread. You should take care to ensure that the work function is not inlined so that the address is definitely in the stack frame below any potential roots.
single: Scheme; thread root
For example, here's the code from the toy Scheme interpreter that registers a thread root and then calls the program:
mps_thr_t thread;
mps_root_t stack_root;
int exit_code;
void *cold = &cold;
res = mps_thread_reg(&thread, arena);
if (res != MPS_RES_OK) error("Couldn't register thread");
res = mps_root_create_thread(&stack_root, arena, thread, cold);
if (res != MPS_RES_OK) error("Couldn't create root");
exit_code = start(argc, argv);
mps_root_destroy(stack_root);
mps_thread_dereg(thread);
pair: root; rank
pair: root; mode
The root mode provides a way for the client to declare various facts about a root that allow the MPS to make optimizations. Roots that are declared to be constant need not be re-scanned, and roots that are declared to be protectable may have barriers placed on them, allowing the MPS to detect whether they have changed.
Note
The MPS does not currently perform either of these optimizations, so root modes have no effect. These features may be added in a future release.
single: root; interface
pair: root; introspection