-
Notifications
You must be signed in to change notification settings - Fork 69
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
Allow roots to be pinned for StickyImmix nursery collections #1108
Allow roots to be pinned for StickyImmix nursery collections #1108
Conversation
Again reinforcing that transitively pinning is still unsupported for the StickyImmix nursery GC (though it should be possible to support it). The main question is how to treat objects in the modbuffer. @wks Can you try this PR out for Ruby to see if it allows objects to move during the nursery GC? |
src/plan/sticky/immix/gc_work.rs
Outdated
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType>; | ||
type TPProcessEdges = GenNurseryProcessEdges<VM, Self::PlanType>; | ||
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType, KIND>; | ||
type TPProcessEdges = UnsupportedProcessEdges<VM>; |
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.
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 think this won't work if a binding uses transitive pinning. Can we use TRACE_KIND_FAST
here? Will that work?
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.
That's what I had I used TRACE_KIND_TRANSITIVE_PIN
in my fork before, but then Eduardo mentioned that transitively pinning has unclear semantics for the modbuffer which I did not consider since ART does not do transitively pinning roots. I decided to make it explicit that transitively pinning is still unsupported instead of having a broken implementation in this PR.
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.
My question was that if this will completely break the Julia binding since it's changed to "unsupported" instead of "just working" tm since Julia disables copying for nursery GCs (i.e. sets PREFER_COPY_ON_NURSERY_GC
to false
).
src/plan/sticky/immix/gc_work.rs
Outdated
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType, KIND>; | ||
type TPProcessEdges = UnsupportedProcessEdges<VM>; |
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.
That breaks Ruby, too. Basically if you set TPProcessEdges
to UnsupportedProcessEdges
, any unmovable roots (delivered via ProcessEdgesWorkRootsWorkFactory::create_process_pinning_roots_work
) will create an work packet that uses UnsupportedProcessEdges
to trace the roots, which crashes.
Suggested changes:
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType, KIND>; | |
type TPProcessEdges = UnsupportedProcessEdges<VM>; | |
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType, DEFAULT_TRACE>; | |
type TPProcessEdges = GenNurseryProcessEdges<VM, Self::PlanType, TRACE_KIND_TRANSITIVE_PIN>; |
And you can remove the generic parameter <KIND>
from StickyImmixNurseryGCWorkContext
, too.
I think the type member type TPProcessEdges
is misleading. It's not really related to "transitive pinning" per se. The semantics is:
- If an edge is updateable (i.e. its target is allowed to move), the GC should create a
ProcessEdgesWorkType
to process that edge. - If an edge cannot be updated (i.e. its target must not move), the GC should create a
TPProcessEdges
to process that edge.
What really happens is that when creating root packets, there are three options:
method | process edges from root to object using | process edges between objects using |
---|---|---|
create_process_edge_roots_work |
ProcessEdgesWorkType |
ProcessEdgesWorkType |
create_process_pinning_roots_work |
TPProcessEdges |
ProcessEdgesWorkType |
create_process_tpinning_roots_work |
TPProcessEdges |
TPProcessEdges |
The kind of root selects the strategy of choosing which packets to use. Non-transitive pinning roots process roots using TPProcessEdges
but after that everything else can be processed with ProcessEdgesWorkType
. Transitive pinning is implemented with forcing all edges reachable from those roots to be traced using TPProcessEdges
.
So the difference is just whether the edge can be updated. I think there is much room for refactoring.
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.
Ohkay -- I should probably have tested that one-line change with ART before making this PR. It probably worked in my fork because I implemented TPProcessEdges
instead of using the "unsupported" one.
This is very confusing. I re-read the create_process_pinning_roots_work
function and it's difficult to reason where the type parameters I
and E
are actually coming from and what they will be when using pinning roots vs transitively pinning roots. Your explanation makes sense but this is nowhere in the doc comment for the ProcessEdgesWorkRootsWorkFactory
type.
I confirmed that with this PR (and the suggested changes I mentioned in the last comment), Ruby is able to pin object during nursery GC, and the GC is also able to move objects during GC. I am not sure whether it works perfectly because the Ruby binding itself has not been fully tested against moving GC, yet. |
Done @wks |
But now with the fix, transitively pinning is actually unsupported (given the issue regarding modbuffer) but MMTk will allow a VM developer to proceed regardless. This can blowup in glorious ways. |
I don't think it conflicts with transitive pinning. They are orthogonal. The fields of the objects in the modbuf should be considered non-pinning roots (unless the VM has something like "potential pinning parents" like Ruby). If an object is added to the modbuf, it simply means the object has been modified before the current GC, and some of its children may point to young objects. Being in the modbuf doesn't mean the field of the object cannot be updated. But if the same young object is also reachable from pinning roots, the object will be pinned; if it's reachable from transitive pinning roots, it and its children will all be pinned. I think the current implementation of if is_nursery_gc() {
// Scan objects in the modbuf and forward pointers
let modbuf = std::mem::take(&mut self.modbuf);
GCWork::do_work(
&mut ScanObjects::<E>::new(modbuf, false, WorkBucketStage::Closure), // Put into the Closure bucket
worker,
mmtk,
)
} As long as the |
Hm. I see. It'd be better if Julia could actually test this PR out to ensure that nothing breaks since neither ART nor Ruby use the transitively pinning roots. |
JikesRVM just seems like it's a flaky CI. This code doesn't even touch the GCs that JikesRVM supports. I don't know why we get the failing instruction not in RVM address error. Should I investigate further? |
Idk why the ready-to-merge check timed out. I just re-ran it. |
https://github.com/mmtk/mmtk-core/actions/runs/8657791364/job/23740512154 It may be an unknown bug. |
@qinsoon I think the ready-to-merge check is broken when we have the binding tests. They're called "extended-tests-<binding name>" instead of "<binding name>-binding-test". |
Yikes. OpenJDK shouldn't time out since this should only change how the pinning/transitively pinning roots work. I'll investigate locally when I have the time. OR This could be the result of the new change where we removed the coordinator. It would be ideal to be able to ssh to the machine and then inspect the deadlocked process. |
Yes. This is possible. By removing the coordinator, the mechanism for triggering GC has changed. There could be some corner cases that are not handled properly, but has not been discovered, yet. |
fbf651b
to
f612af4
Compare
@qinsoon I'm not sure if the ready to merge check was fixed properly. It seems like it's still waiting on something |
Not sure what is happening there. The problem of wrong ignored action names was fixed. |
Yeah. It was actually a mistake. #1119 should fix it. The action name for OpenJDK was changed due to the reused workflow. So the merge check was actually waiting for the OpenJDk tests to finish (as it was not ignored). |
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
This PR allows StickyImmix to properly deal with transitive pinning trace. Before this PR, StickyImmix may move objects in the transitive pinning trace, as it simply uses
GenNurseryProcessEdges
which allows moving. With this PR, for transitive pinning trace, StickyImmix usesGenNurseryProcessEdges<..., TRACE_KIND_TRANSITIVE_PIN>
. This PR fixes #1097.