Skip to content

GCWorker instead of VMWorkerThread #1390

@wks

Description

@wks

Many internal functions as well as public API functions have tls: VMWorkerThread as argument. Although they were intended for VM bindings to have thread-local contexts inside those functions, they are cumbersome to use, especially when we need to add work packets.

We can grep the strong tls: VMWorkerThread to get a full list. Here I give some examples:

  • Plan::prepare(&mut self, tls: VMWorkerThread)
  • Plan::release(&mut self, tls: VMWorkerThread)
  • Plan::end_of_gc(&mut self, tls: VMWorkerThread)
  • xxx_mutator_prepare<VM: VMBinding>(mutator: &mut Mutator<VM>, tls: VMWorkerThread)
  • xxx_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, tls: VMWorkerThread)
  • Collection::stop_all_mutators<F>(tls: VMWorkerThread, mutator_visitor: F)
  • `Collection::resume_mutators(tls: VMWorkerThread)
  • Scanning::scan_object<...>(tls: VMWorkerThread, ...)
  • Scanning::scan_vm_specific_roots(tls: VMWorkerThread, ...)

One thing is for sure that those functions are all executed by GC worker threads, (probably with Scanning::scan_object also being called by mutator threads in write barriers). tls: VMWorkerThread is the VM-level thread-local context of a GC worker thread. Admittedly, it is there to allow the VM binding use such context.

But the MMTk-level thread-local context of a GC worker thread is the GCWorker<VM>, and provides accesses to the GC-related functionality, such as a reference to the scheduler, and the ability to add work packets. It often happens that

  • Inside MMTk core, some plans want to add work packets in one of those functions. For example, a ConcurrentImmix plan may add work packets to clear side metadata in parallel in ConcurrentImmix::prepare.
  • In VM bindings, some VMs want to add work packets for processing VM-specific data structures. For example, the OpenJDK binding wants to add work packets to scan VM-specific roots, and fix nmethod relocations.

Currently, both the VM binding and parts of mmtk-core rely on memory_manager::add_work_packet(mmtk, bucket, packet), and rely on the presence of mmtk: &'static MMTK<VM>.

  • In MMTk core, ProcessEdgesWorkRootsWorkFactory holds a reference to &'static MMTK<VM>, and it gets the mmtk reference from GCWork::do_work(&mut self, worker, mmtk).
  • In the OpenJDK binding, scan_vm_specific_roots passes the global SINGLETON to memory_manager::add_work_packet(mmtk, bucket, packet).

And when we want to add work packets in ConcurrentImmix::prepare, we found that there is no reference to the MMTK instance, and there is no reference to a GCWorker<VM> which contains a reference to the MMTK instance. Fortunately, (and weirdly,) ImmixSpace has a reference to scheduler: GCWorkScheduler (but CopySpace doesn't. Neither do LargeObjectSpace, ImmortalSpace nor VMSpace have a reference to the scheduler). The method ImmixSpace::scheduler() was particularly used for adding work packets, working around the limitation.

Why was there tls: VMWorkerThread?

As for the API, there was an assumption since the beginning of the development of Rust MMTk, that the VM is usually implemented in a native language, usually not Java. So it made more sense to pass the VM-specific tls: VMWorkerThread instead of the MMTk-specific GCWorker.

As for the internal parts of mmtk-core, I guess the mmtk-core was trying to pass as few parameters as possible between components of mmtk-core. It passed tls: VMWorkerThread around just for making the MMTkCore-to-VMBinding calls possible.

But Plan::prepare has the tls parameter for no reason. It passes it to CommonPlan::prepare, then down to BasePlan::prepare, and then the _tls parameter` is unused.

What should we do?

All methods of Plan that are supposed to be called only by GC worker threads should have worker: &mut GCWorker<VM> as the second parameter (after self) instead of tls: VMWorkerThread.

And remove ImmixSpace::scheduler() which is a workaround.

In API functions, mainly in Collection and Scanning, pass worker: GCWorker<VM> instead of tls: VMWorkerThread.

We need to think carefully about Scanning::scan_object and Scanning::scan_object_and_trace_edges. As discussed in #1375, we may loosen tls: VMWorkerThread to tls: VMThread. But that goes against this proposal. Making it Either<GCWorker<VM>, VMMutatorThread> is overly complex and may add an overhead of wrapping, while object scanning is usually part of the hottest tracing loop in MMTK. We may make them an exception and pass tls: VMThread instead, requiring the VM binding to check if it is a mutator thread or GC worker thread. Alternatively, we add a method Scanning::scan_object_for_mutator specifically for this purpose, which was also a proposal in #1375.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions