-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
No ProcessEdgesWork in API functions #611
Conversation
Notably, mmtk-v8 no longer mentions ProcessEdgesWork.
5b73a30
to
6aaeca5
Compare
Notably, mmtk-v8 no longer mentions ProcessEdgesWork.
6aaeca5
to
ed1d16c
Compare
binding-refs |
Root-scanning functions no longer pass concrete work packet types as type parameters. Particularly, the API no longer exposes the ProcessEdgesWork trait to the VM bindign. Instead, mmtk-core passes factory objects and call-back functions so that VM bindings can call back to mmtk-core to report lists of root edges and the event that a mutator has stopped. With this change, VM bindings should not create work packets to handle root edges directly. Instead, the factories and call-backs defined in mmtk-core shall create those packets.
ed1d16c
to
6b45222
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -261,7 +263,7 @@ impl<E: ProcessEdgesWork> VMProcessWeakRefs<E> { | |||
impl<E: ProcessEdgesWork> GCWork<E::VM> for VMProcessWeakRefs<E> { | |||
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) { | |||
trace!("ProcessWeakRefs"); | |||
<E::VM as VMBinding>::VMCollection::process_weak_refs::<E>(worker); | |||
<E::VM as VMBinding>::VMCollection::process_weak_refs(worker); // TODO: Pass a factory/callback to decide what work packet to create. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What changes are needed for this TODO
? Adding the factory as an argument should be trivial, but we might need to add more methods to the factory trait?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I wanted to talk about this yesterday, but forgot.
The problem is, this work packet VMProcessWeakRefs
was introduced long time ago, but it still does nothing. VMCollection::process_weak_refs
is a no-op, and it is not even overridden in V8. So I doubt if it is helpful to fix this work packet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another problem is, the work packet for processing weak refs may or may not be the same as ProcessEdgesWork
. I elaborated that in #604
My current finding is, what weak-reference processing needs is simpler than ProcessEdgesWork
, even simpler than the TracingDelegate
I mentioned in that issue. The weak reference processor only needs the capability to call trace_object
. For our current GC algorithms which are all based on tracing, the weak reference processor should create ScanObjects
work packet for all the objects enqueued with trace_object
. This part is the same as ProcessEdgesWork
. But I am not sure for RC-based plans.
I think we can leave the code like this, and change it when a VM (such as V8) actually started using this work packet.
We also discussed about refactoring the weak reference processors to adapt to VMs where weak references are not represented as objects (such as Ruby where the primitive mechanism for weak references is WeakMap
). If that is done, I think this work packet may be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could ask @wenyuzhao about how his V8 branch deals with the type parameter W
in process_weak_refs()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to do anything for this PR. But it would be good to know that the design can deal with this issue in the future.
src/scheduler/gc_work.rs
Outdated
@@ -277,7 +279,8 @@ impl<E: ProcessEdgesWork> ScanStackRoots<E> { | |||
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanStackRoots<E> { | |||
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) { | |||
trace!("ScanStackRoots"); | |||
<E::VM as VMBinding>::VMScanning::scan_thread_roots::<E>(); | |||
let factory = ProcessEdgesWorkRootsWorkFactory::<E>::new_boxed(mmtk); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am guessing creating the factory here is just a temporary step for the PR, and eventually, the plan should be responsible for creating the factory. Is this understanding correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Eventually, it will be the plan's responsibility to create the factory it needs. The plan may even create a different factory for each different kind of GC (such as nursery vs full-heap, as well as concurrent marking).
I also plan to refactor the ProcessEdgesWork
, but not in this PR. This PR only changes the VM-facing API.
src/vm/scanning.rs
Outdated
fn create_process_node_roots_work(&self, nodes: Vec<ObjectReference>); | ||
|
||
/// Create a copy of the factory itself. | ||
fn fork(&self) -> Box<dyn RootsWorkFactory>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make RootsWorkFactory: Sync
, and use Arc<dyn RootsWorkFactory>
to pass the factory to the binding? It will be more clear that the factory can be shared among threads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since ProcessEdgesWorkRootsWorkFactory
has only one field mmtk: &'static MMTK<E::VM>
, we can further make RootsWorkFactory: Copy
to avoid heap allocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I thought cloning an Arc
would be more expensive than cloning a Box
because of the underlying atomic memory operation. But after a second thought, I think that cost should be trivial, and the RootsWorkFactory
should be freely sharable among threads because both of the required create_process_{node,edge}_roots_work
methods take an immutable &self
reference.
I'll try to change it to Arc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arc
would be slightly different than this fork()
model:
- With
Arc
, the factory probably won't have any mutable state (unless lock is used). - If the factory has any state,
fork()
may choose to create a fresh instance or inherit the state. But withArc
, as they point to the same instance, the state is shared.
I am not sure which is more suitable when we have other RootsWorkFactory
impls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
src/vm/scanning.rs
Outdated
fn create_process_node_roots_work(&self, nodes: Vec<ObjectReference>); | ||
|
||
/// Create a copy of the factory itself. | ||
fn fork(&self) -> Box<dyn RootsWorkFactory>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since ProcessEdgesWorkRootsWorkFactory
has only one field mmtk: &'static MMTK<E::VM>
, we can further make RootsWorkFactory: Copy
to avoid heap allocation.
@wenyuzhao Regarding heap allocation, I am thinking about several design choices.
If it is I'll try some possibilities, especially using |
This allows unboxed instances to be passed to Scanning::scan_XXX_roots. RootsWorkFactory now requires the Clone trait, and removed fork().
I switched to a different approach. We now pass Now |
@wks Is there any change you would add to this pull request? Otherwise, we can merge it. |
@qinsoon No, but the CI tests for mmtk-jikesrvm binding is failing for some reasons. |
* Update for upstream PR mmtk/mmtk-core#611 Notably, mmtk-v8 no longer mentions ProcessEdgesWork.
This PR removes the
ProcessEdgesWork
trait from the root-scanning API.Currently, mmtk-core passes the
<E: ProcessEdgesWork>
type to the binding during root scanning, and the binding creates the work packets using theProcessEdgesWork::new
trait method. This couples root scanning with theProcessEdgesWork
trait.But in reality, the
ProcessEdgesWork
trait may not be applicable to some root-scanning tasks.ProcessEdgesWork
, such astrace_object
,process_node
orflush
. It merely visits all root edges. Actually it only visits the objects the root edges refer to and does not forward the root edges because the concurrent marking is non-copying.This PR introduces a trait:
A
RootsWorkFactory
instance is passed to the VM for scanning roots. The VM reports root edges and root nodes using this object. In this way, the VM no longer needs to know what work packet mmtk-core uses to handle the roots.The
fork
method is for sharing the factory among multiple threads. Some VMs (such as OpenJDK and JikesRVM) create additional work packets to scan different kinds of roots. For example, when scanning VM-specific roots, OpenJDK will createScanUniverseRoots
,ScanJNIHandlesRoots
,ScanObjectSynchronizerRoots
, ..., and JikesRVM will createScanStaticRoots
andScanGlobalRoots
. The VM can fork the factory so that each such work packet can hold a copy.