Skip to content

Mutator-related GCWork and Send/Sync traits #543

Open
@wks

Description

@wks

Although Mutator is supposed to be a thread-local data structure of a mutator thread, some GCWork instances modifies the Mutator instances on behalf of the mutators. These include:

  • PrepareMutator
  • ReleaseMutator
  • ScanStackRoot

Remember that GCWork implements Send but not Sync. (It is Send because it can be distributed among workers; it is not Sync because a GCWork is supposed to be visible to only one thread at a time.)

Currently, those structs contain &mut mutator. PrepareMutator is a GCWork, and it needs to implements Send. Because it contains a &mut Mutator, it requires Mutator to implement Send, too. Similar is true for ReleaseMutator and ScanStackRoot.

p.s. Changing &mut Mutator to &Mutator will still require Mutator to implement Send, because PrepareMutator implements Send.

Can we make Mutator implement Send?

Mutator currently implements Send, but we probably don't want Mutator to implement Send, because it is supposed to be local to a mutator thread. (Update: As we discussed later in #543 (comment), it probably should implement Send after all. The Send trait may make sense during initialization.)

If Mutator implements Send (the status quo), there will be other consequences:

  1. PrepareMutator and its friends contain &mut mutator.
    • It requires Mutator to be Send
    • Currently, MutatorContext is declared as pub trait MutatorContext<VM: VMBinding>: Send + 'static
  2. Mutator contains a Box<dyn Barrier> which can contain a ObjectRememberingBarrier.
    • It will require Barrier to implement Send.
    • Currently, Barrier is declared as pub trait Barrier: 'static + Send
  3. ObjectRememberingBarrier contains a &MMTK.
    • This requiers &MMTK to implement Send, which in turn requires MMTK to implement Sync. (In Rust, T implements Sync if and only if &T implements Send)
  4. MMTK contains an Arc<GCWorkScheduler>
  5. GCWorkScheduler contains many WorkBucket.
  6. WorkBucket contains many PrioritizedWork.
  7. PrioritizedWork contains a Box<dyn GCWork<VM>>. Because it is dyn, it can be any GCWork, including PrepareMutator and others.
    • Because MMTK implements Sync, GCWork needs to implement Sync. This contradicts with our design that GCWork does not implement Sync.

From a different point of view, having ObjectRememberingBarrier in Mutator closes the loop from one work to any other work. Any work that contains &mut mutator can potentially see any other GCWork instances. This allows one worker to peek into another worker's work. Remember that GCWork is not Sync. Not implementing Sync means it cannot be visible to two threads at the same time.

But why is it working so far?

In scheduler.rs, there is a line:

unsafe impl<VM: VMBinding> Sync for GCWorkScheduler<VM> {}

That line broke the dependency chain shown above.

Solution?

Let's think about what should implement Sync. Shared data structures need to implement Sync.

  • MMTK needs to implement Sync.
  • GCWorkScheduler needs to implement Sync.
  • GCWorkerShared needs to implement Sync.

But we shouldn't need to write anything because the Rust compiler can figure out if MMTK satisfies the need of Sync.

And what shouldn't implement Sync?

  • Mutator should not implement Sync, because it should be local to a mutator. But GC also needs to mutate it.
    • Mutator::barrier: This should probably be not Sync, because only the mutator thread ever execute it. GC never touches it. And to some degree, it shouldn't exist at all, because Mutator has a reference to Plan. A barrier is "what to do when reading/writing a reference field". It is the responsibility of the Plan which describes the GC algorithm.
    • Mutator::allocator: This probably belongs to the part shared between the mutator thread and GC threads.

Should we do the same refactoring as we did for GC workers, i.e. splitting Mutator into a part shared with GC threads, and the part local to the mutator thread? Mutator is #[repr(C)], and the structure is visible to the VM.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P-normalPriority: Normal.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions