Skip to content

Ephemeral and non-keep-alive pinning #661

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

Closed
wks opened this issue Sep 7, 2022 · 5 comments
Closed

Ephemeral and non-keep-alive pinning #661

wks opened this issue Sep 7, 2022 · 5 comments

Comments

@wks
Copy link
Collaborator

wks commented Sep 7, 2022

As discussed in #129, we need an API for VMs to pin objects. This API is usually used for IO. If a byte buffer is used for a system call, GC must not move it until the system call finishes, and GC should not reclaim them, either.

However, in addition to the explicit pinning API for the VM, there are other sources of object pinning, with different properties.

One is conservative stack scanning. Words conservatively considered as references must not be changed (it may actually be value), and the object must be pinned. Because they are roots, they keep the objects alive.

The other is for the need of Ruby. In Ruby, some third-part C extensions were developed before copying GC, therefore those custom T_DATA objects with custom dmark functions are not aware that the adjacent objects those objects point to can be moved. Therefore, we must pin the neighbours of those T_DATA objects. However, this kind of pinning does not keep the objects alive. It merely states that if the neighbours are still alive, they should not be moved; but if the T_DATA objects themselves are dead, their neighbours can just die.

One thing in common between those two cases is that the pinning is local to the current GC. For example, if a word is on the stack in one GC, it may not appear on the next GC. A T_DATA may be already dead when the GC starts, so it will not live to the next GC, but it will still pin its neighbours.

To summary, the different flavours of pinning include:

Kind Duration Keep object alive
pin/unpin API until explicitly unpin yes
conservative stack this GC yes
old T_DATA object this GC no

Implementing ephemeral pinning

To implement ephemeral pinning (pinned only during this GC), we remember which objects we newly pinned at the beginning of this GC, and unpin them at the end of this GC.

Implementing non-keep-alive pinning

I think pinning doesn't necessarily keep objects alive. Implementation-wise, "pinning" and "marked/forwarded" should be two different metadata.

@qinsoon
Copy link
Member

qinsoon commented Sep 7, 2022

I thought we only provide non-keep-alive pinning. If a binding only wants to pin an object for only one GC, they need to explicitly unpin after the GC.

@wks
Copy link
Collaborator Author

wks commented Sep 7, 2022

If a binding only wants to pin an object for only one GC, they need to explicitly unpin after the GC.

This may be problematic for non-keep-alive (i.e. non-retaining) pinning. There are two problems.

  1. Those pinned object may die. If an object dies, it is no longer there.
  2. The objects that should be pinned are the neighbours of the T_DATA objects. We can't keep track of all of its fields all the time. That requires a write barrier that pins objects on every store, which is inefficient. And Ruby actually discourages C extensions to use write barriers unless necessary (the good thing is Ruby encourages not to touch ObjectRefrence at all in C).

What I think should be appropriate for Ruby is that mmtk-core shall provide a pre-pin phase where the VM enumerate all objects that cannot be moved (but not kept alive) for this GC. For Ruby, it will enumerate all T_DATA objects and pin their neighbours. After the GC, some T_DATA will be reclaimed, and their neighbours are reclaimed, too, if not reachable through other paths. Because those neighbours are reclaimed, there is no chance (and no need) to "unpin" them.

I think the chance to unpin those object should be the same as the time when conservative stack roots are unpinned, i.e. after GC decides the life and death of an object, but before resuming the mutator. The only difference is that before unpinning, it should test if the object is still alive.

@qinsoon
Copy link
Member

qinsoon commented Sep 14, 2022

We discussed this issue in today's MMTk-Julia meeting. Steve suggested the semantics of pinning is stronger than just not moving the object. Usually in a language implementation, pinning means someone is using the object, so the object is alive and should not be moved. Also the fact that someone is using the object means the object should be treated as a root as well (which will keep it alive). With these discussed, Steve suggested a binding could simply use root objects for whatever they want to pin, and we do not really need a pinning API (we still need to implement pinning inside MMTk).

@wks
Copy link
Collaborator Author

wks commented Sep 14, 2022

@qinsoon I agree that if the user explicitly pins an object, the user must be using that object, and it must be in the root set. I also agree that the VM can implement per-object pinning by adding/removing objects from a pinned root set, and reporting them to MMTk as unmovable roots, therefore not requiring a dedicated pinning API from MMTk.

The Ruby's case is special. The main difference is that the "neighbours or T_DATA" are not explicitly pinned. Actually, we don't know if anyone is using the object. So the semantics should be:

  1. If anyone is using that object, it should not move;
  2. If not, it is garbage, and it should be recycled.

In vanilla Ruby, the fact of whether an object is used and whether an object is a neighbour of T_DATA is discovered gradually. During the marking phase, the "neighbours of T_DATA" are marked with rb_gc_mark which both marks and pins the object. (FYI, other objects are marked with rb_gc_mark_movable.) Then during the compaction phase, we already know which objects cannot move.

But API-wise, it should be enough to report such "pinned but not rooted" objects separately.

@wks
Copy link
Collaborator Author

wks commented Mar 8, 2023

The PR #703 added an API for supporting such non-keep-alive pinning. It has been used by mmtk-ruby to handle "potential pinning parents".

Closing this issue...

@wks wks closed this as completed Mar 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants