Skip to content

Cleaning up OOP-style inheritance and the impls of Deref #587

Open
@wks

Description

@wks

TL;DR: Several types in MMTk core use inheritance in OOP. This is not idiomatic in Rust. In the long term, we should switch to a more idiomatic style, such as composition instead of inheritance.

In practice, we should tread lightly, and do refactoring in small steps. Not all such cases need to be eliminated, because there may not always be a better solution.

Examples:

The following types use inheritance.

  • Plan: concrete plans extend CommonPlan extends BasePlan
  • Space: concrete spaces extend CommonSpace
  • ProcessEdgesWork: Any concrete ProcessEdgesWork always contains a ProcessEdgesBase
  • PageResources: Any concrete page resource contains a ComonPageResource

Analysis

Inheritance vs composition

An instance of a concrete type contains an instance of an abstract type because the abstract type holds common information shared by multiple concrete types. For example, all spaces should have name.

Conversely, inheritance also means each subclass customises the abstract class in a certain way. For example, all ProcessEdgesWork have an incoming queue edges to be processed, and an outgoing queue of objects to be scanned, as shown in ProcessEdgesBase, but each concrete ProcessEdgesWork customises the way how to process each edge.

If we view the "inheritance" relation as a form of "customisation", we may think that there is only one kind of ProcessEdgesWork, but each instance allow some behaviour to be customised. And there are many different ways of customisation besides OOP-style inheritence. One possibility is composition. If we consider trace_object as a responsibility of Plan, then all ProcessEdgesWork are the same. ProcessEdgesWork only calls into Plan to do the actual tracing.

Both #575 and #570 attempt to eliminate plan-specific ProcessEdgesWork implementations. They are examples of replacing inheritance with composition.

Methods for MMTK accidentally added to BasePlan

BasePlan is special. It comes from the Plan abstract class in the JikesRVM version of MMTk. Because Java MMTk didn't have the global instance type MMTK, globally shared data fields are fields of the Plan class.

Most fields in BasePlan are not really plan-specific. For example,

  • BasePlan::initialized indicates if MMTk is initialised. No matter what plan we use, MMTk always needs to be initialised.
  • BasePlan::options is the options of the MMTK instance set by the command-line or environment variables. They are not plan-specific.
  • BasePlan::gc_requester is the synchroniser where mutator threads can notify GC threads to start GC. All plans need this.

The only few fields that should conceptually belong to BasePlan are the three spaces, namely code_space, code_lo_space and ro_space because a plan contains many spaces.

Those shared fields should have been fields of struct MMTK.

Deref

According to Rust doc: https://doc.rust-lang.org/std/ops/trait.Deref.html

... On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.

In mmtk-core, the following types implement Deref and/or DerefMut.

Group 1:

  • InitializeOnce<T> (to T)

Group 2:

  • UnsafeOptionsWrapper (to Options)
  • MMTKOption<T> (to T)

Group 3:

  • GenNurseryProcessEdges (to ProcessEdgesBase)
  • SanityGCProcessEdges (to ProcessEdgesBase)
  • SFTProcessEdges (to ProcessEdgesBase)
  • FreeListPageResource (to CommonFreeListPageResource)
  • Type defined by define_vm_metadata_spec (to MetadataSpec)

Group 1, i.e. InitializeOnce<T>, is a proper use of Deref, becaue IntializeOnce<T> is a kind of smart pointer. It is responsible for resource management.

In Group 2, UnsafeOptionsWrapper and MMTKOption work around static initialisation. See #541

Group 3 contains types that attempt to implement inheritance of object-oriented programming in Rust. Those belong to the category discussed here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-generalArea: all code base (issues with this label may be divided into more concrete issues)C-refactoringCategory: RefactoringP-normalPriority: Normal.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions