Skip to content

Commit d09c58a

Browse files
committed
Update tutorial
1 parent 70896de commit d09c58a

File tree

3 files changed

+181
-34
lines changed

3 files changed

+181
-34
lines changed

docs/tutorial/code/mygc_semispace/gc_work.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,95 @@ use crate::vm::VMBinding;
55
use std::ops::{Deref, DerefMut};
66
// ANCHOR_END: imports
77

8-
// ANCHOR: workcontext
8+
// ANCHOR: workcontext_sft
99
pub struct MyGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
1010
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext<VM> {
1111
type VM = VM;
1212
type PlanType = MyGC<VM>;
1313
type ProcessEdgesWorkType = SFTProcessEdges<Self::VM>;
1414
}
15-
// ANCHOR_END: workcontext
15+
// ANCHOR_END: workcontext_sft
16+
17+
// ANCHOR: workcontext_plan
18+
use crate::plan::transitive_closure::PlanProcessEdges;
19+
use crate::policy::gc_work::DEFAULT_TRACE;
20+
pub struct MyGCWorkContext2<VM: VMBinding>(std::marker::PhantomData<VM>);
21+
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext2<VM> {
22+
type VM = VM;
23+
type PlanType = MyGC<VM>;
24+
type ProcessEdgesWorkType = PlanProcessEdges<Self::VM, MyGC<VM>, DEFAULT_TRACE>;
25+
}
26+
// ANCHOR: workcontext_plan
27+
28+
use crate::util::{Address, ObjectReference};
29+
use crate::util::copy::CopySemantics;
30+
use crate::MMTK;
31+
use crate::policy::space::Space;
32+
33+
// ANCHOR: mygc_process_edges
34+
pub struct MyGCProcessEdges<VM: VMBinding> {
35+
plan: &'static MyGC<VM>,
36+
base: ProcessEdgesBase<VM>,
37+
}
38+
// ANCHOR_END: mygc_process_edges
39+
40+
// ANCHOR: mygc_process_edges_impl
41+
impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
42+
type VM = VM;
43+
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
44+
let base = ProcessEdgesBase::new(edges, roots, mmtk);
45+
let plan = base.plan().downcast_ref::<MyGC<VM>>().unwrap();
46+
Self { base, plan }
47+
}
48+
49+
#[inline]
50+
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
51+
if object.is_null() {
52+
return object;
53+
}
54+
if self.plan.tospace().in_space(object) {
55+
self.plan.tospace().trace_object::<Self>(
56+
self,
57+
object,
58+
Some(CopySemantics::DefaultCopy),
59+
self.worker(),
60+
)
61+
} else if self.plan.fromspace().in_space(object) {
62+
self.plan.fromspace().trace_object::<Self>(
63+
self,
64+
object,
65+
Some(CopySemantics::DefaultCopy),
66+
self.worker(),
67+
)
68+
} else {
69+
self.plan.common.trace_object::<Self>(self, object)
70+
}
71+
}
72+
}
73+
// ANCHOR_END: mygc_process_edges_impl
74+
75+
// ANCHOR: mygc_process_edges_deref
76+
impl<VM: VMBinding> Deref for MyGCProcessEdges<VM> {
77+
type Target = ProcessEdgesBase<VM>;
78+
#[inline]
79+
fn deref(&self) -> &Self::Target {
80+
&self.base
81+
}
82+
}
83+
84+
impl<VM: VMBinding> DerefMut for MyGCProcessEdges<VM> {
85+
#[inline]
86+
fn deref_mut(&mut self) -> &mut Self::Target {
87+
&mut self.base
88+
}
89+
}
90+
// ANCHOR_END: mygc_process_edges_deref
91+
92+
// ANCHOR: workcontext_mygc
93+
pub struct MyGCWorkContext3<VM: VMBinding>(std::marker::PhantomData<VM>);
94+
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext3<VM> {
95+
type VM = VM;
96+
type PlanType = MyGC<VM>;
97+
type ProcessEdgesWorkType = MyGCProcessEdges<Self::VM>;
98+
}
99+
// ANCHOR: workcontext_mygc

docs/tutorial/code/mygc_semispace/global.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,18 @@ use std::sync::Arc;
2929
// Remove #[allow(unused_imports)].
3030
// Remove handle_user_collection_request().
3131

32+
use mmtk_macro_trace_object::PlanTraceObject;
33+
3234
// Modify
3335
// ANCHOR: plan_def
36+
#[derive(PlanTraceObject)]
3437
pub struct MyGC<VM: VMBinding> {
3538
pub hi: AtomicBool,
39+
#[trace(CopySemantics::DefaultCopy)]
3640
pub copyspace0: CopySpace<VM>,
41+
#[trace(CopySemantics::DefaultCopy)]
3742
pub copyspace1: CopySpace<VM>,
43+
#[fallback_trace]
3844
pub common: CommonPlan<VM>,
3945
}
4046
// ANCHOR_END: plan_def

docs/tutorial/src/mygc/ss/collection.md

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ method `schedule_common_work()` that will add common work packets for you.
4343

4444
To use `schedule_common_work()`, first we need to create a type `MyGCWorkContext`
4545
and implement the trait `GCWorkContext` for it. We create `gc_work.rs` and add the
46-
following implementation. Note that we do not set a specific `ProcessEdgesWorkType`
47-
and we will use the default [`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html),
46+
following implementation. Note that we will use the default
47+
[`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html),
4848
which is a general work packet that a plan can use to trace objects. For plans
4949
like semispace, `SFTProcessEdges` is sufficient. For more complex GC plans,
5050
one can create and write their own work packet that implements the `ProcessEdgesWork` trait.
51+
We will discuss about this later, and discuss the alternatives.
5152

5253
```rust
53-
{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext}}
54+
{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext_sft}}
5455
```
5556

5657
Then we implement `schedule_collection()` using `MyGCWorkContext` and `schedule_common_work()`.
@@ -111,35 +112,6 @@ there aren't any preparation steps for the mutator in this GC.
111112
In `create_mygc_mutator()`, find the field `prep_func` and change it from
112113
`mygc_mutator_noop()` to `mygc_mutator_prepare()`.
113114

114-
115-
## Scan objects
116-
117-
Next, we'll add the code to allow the plan to collect garbage - filling out
118-
functions for work packets.
119-
120-
In `gc_work.rs`, add a new method to `ProcessEdgesWork for MyGCProcessEdges`,
121-
`trace_object(&mut self, object: ObjectReference)`.
122-
This method should return an ObjectReference, and use the
123-
inline attribute.
124-
Check if the object passed into the function is null
125-
(`object.is_null()`). If it is, return the object.
126-
Otherwise, check which space the object is in, and forward the call to the
127-
policy-specific object tracing code. If it is in neither space, forward the
128-
call to the common space and let the common space to handle object tracing in
129-
its spaces (e.g. immortal or large object space):
130-
131-
```rust
132-
{{#include ../../../code/mygc_semispace/gc_work.rs:trace_object}}
133-
```
134-
135-
Add two new implementation blocks, `Deref` and `DerefMut` for
136-
`MyGCProcessEdges`. These allow `MyGCProcessEdges` to be dereferenced to
137-
`ProcessEdgesBase`, and allows easy access to fields in `ProcessEdgesBase`.
138-
139-
```rust
140-
{{#include ../../../code/mygc_semispace/gc_work.rs:deref}}
141-
```
142-
143115
## Release
144116

145117
Finally, we need to fill out the functions that are, roughly speaking,
@@ -178,6 +150,91 @@ will then go to the new tospace.
178150
Delete `mygc_mutator_noop()`. It was a placeholder for the prepare and
179151
release functions that you have now added, so it is now dead code.
180152

153+
## ProcessEdgesWork for MyGC
154+
155+
[`ProcessEdgesWork`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/trait.ProcessEdgesWork.html)
156+
is the key work packet for tracing objects in a GC. A `ProcessEdgesWork` implementation
157+
defines how to trace objects, and how to generate more work packets based on the current tracing
158+
to finish the object closure.
159+
160+
`GCWorkContext` specifies a type
161+
that implements `ProcessEdgesWork`, and we used `SFTProcessEdges` earlier. In
162+
this section, we discuss what `SFTProcessEdges` does, and what the alternatives
163+
are.
164+
165+
### Approach 1: Use `SFTProcessEdges`
166+
167+
[`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html) dispatches
168+
trace objects to each space through [Space Function Table (SFT)](https://www.mmtk.io/mmtk-core/mmtk/policy/space/trait.SFT.html).
169+
As long as all the policies in a plan provides an implementation of `sft_trace_object()` in their SFT implementation,
170+
the plan can use `SFTProcessEdges`. Currently most policies provide an implementation for `sft_trace_object()`, except
171+
mark compact and immix. Those two policies use multiple GC traces, and due to the limitation of SFT, SFT does not allow
172+
multiple `sft_trace_object()` for a policy.
173+
174+
`SFTProcessEdges` is the simplest approach when all the policies support it. Fortunately, we can use it for our GC, semispace.
175+
176+
### Approach 2: Derive `PlanTraceObject` and use `PlanProcessEdges`
177+
178+
`PlanProcessEdges` is another general `ProcessEdgesWork` implementation that can be used by most plans. When a plan
179+
implements the [`PlanTraceObject`](https://www.mmtk.io/mmtk-core/mmtk/plan/transitive_closure/trait.PlanTraceObject.html),
180+
they can use `PlanProcessEdges`.
181+
182+
You can manually provide an implementation of `PlanTraceObject` for `MyGC`. But you can also use the derive macro MMTK provides,
183+
and the macro will generate an implementation of `PlanTraceObject`:
184+
* add `#[derive(PlanTraceObject)]` for `MyGC` (import the macro properly: `use mmtk_macro_trace_object::PlanTraceObject`)
185+
* add `#[trace(CopySemantics::Default)]` to both copy space fields, `copyspace0` and `copyspace1`. This tells the macro to generate
186+
trace code for both spaces, and for any copying in the spaces, use `CopySemantics::DefaultCopy` that we have configured early.
187+
* add `#[fallback_trace]` to `common`. This tells the macro that if an object is not found in any space with `#[trace]` in ths plan,
188+
try find the space for the object in the 'parent' plan. In our case, we fall back to the `CommonPlan`, as the object may be
189+
in large object space or immortal space in the common plan. `CommonPlan` also implements `PlanTraceObject`, so it knows how to
190+
find a space for the object and trace it in the same way.
191+
192+
With the derive macro, your `MyGC` struct should look like this:
193+
```rust
194+
{{#include ../../../code/mygc_semispace/global.rs:plan_def}}
195+
```
196+
197+
Once this is done, you can specify `PlanProcessEdges` as the `ProcessEdgesWorkType` in your GC work context:
198+
```rust
199+
{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext_plan}}
200+
```
201+
202+
### Approach 3: Implement your own `ProcessEdgesWork`
203+
204+
Apart from the two approaches above, you can always implement your own `ProcessEdgesWork`. This is
205+
an overkill for simple plans like semi space, but is probably necessary for more complex plans.
206+
We discuss how to implement it for `MyGC`.
207+
208+
Create a struct `MyGCProcessEdges<VM: VMBinding>` in the `gc_work` module. It includes a reference
209+
back to the plan, and a `ProcessEdgesBase` field:
210+
```rust
211+
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges}}
212+
```
213+
214+
Implement `ProcessEdgesWork` for `MyGCProcessEdges`. As most methods in the trait have a default
215+
implemetation, we only need to implement `new()` and `trace_object()` for our plan. However, this
216+
may not be true when you are implement for other GC plans. It would be better to check the default
217+
implementation of `ProcessEdgesWork`.
218+
219+
For `trace_object()`, what we do is similar to the approach above (except that we need to write the code
220+
ourselves rather than letting the macro to generate it for us). We try figure out
221+
which space the object is in, and invoke `trace_object()` for the object on that space. If the
222+
object is not in any of the semi spaces in the plan, we forward the call to `CommonPlan`.
223+
```rust
224+
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges_impl}}
225+
```
226+
227+
We would also need to implement `Deref` and `DerefMut` to our `ProcessEdgesWork` impl to be
228+
dereferenced as `ProcessEdgesBase`.
229+
```rust
230+
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges_deref}}
231+
```
232+
233+
In the end, use `MyGCProcessEdges` as `ProcessEdgesWorkType` in the `GCWorkContext`:
234+
```rust
235+
{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext_mygc}}
236+
```
237+
181238
## Summary
182239

183240
You should now have MyGC working and able to collect garbage. All three

0 commit comments

Comments
 (0)