Matt Valentine-House c78695fc36 [ruby/mmtk] Keep Ruby's GC disable state across a forced GC.start
GC.start has to run even when GC is disabled, so we enable collection,
run the GC and then disable it again. #134 fixed the crash we saw when
another Ractor requested a GC before we could re-disable, but we still
restore whatever state we saw before the GC.

That's a problem when another Ractor changes the state in the meantime.
If it enables GC while we're collecting, we disable it again on its
behalf and GC stays off for good. Ractor.new disables and enables GC
around thread allocation, so this isn't hard to hit. GC.disable and the
internal gc_disable also share a single flag in mmtk-core, so one can
undo the other.

In this commit we track the internal flag, the user flag and any forced
GC.start requests in the binding, and mirror the combined state into
mmtk-core. A forced GC.start only enables collection until the next
pause ends, and we reapply the state in resume_mutators before any
mutator can request another GC.

GC workers also toggle the internal flag during a pause, through
DURING_GC_COULD_MALLOC_REGION. That reads the flag and then sets it, so
two workers running in parallel can leave it set. mmtk-core used to
ignore these because it can't disable collection mid-pause, so we do the
same and drop internal changes between stop_all_mutators and
resume_mutators.

This replaces the retry loop from #134, if we fail to disable because a
GC is pending, the end of that GC puts the state right.

AtomicU64::update needs Rust 1.95, so we declare that in Cargo.toml.

https://github.com/ruby/mmtk/commit/7430ebeaa6
2026-11-06 22:13:40 +00:00
..
2026-11-05 15:44:28 -05:00
2026-10-28 18:45:56 -04:00
…

Ruby's Garbage Collectors

This directory contains implementations for Ruby's garbage collector (GC). The GC implementations use the Modular GC API to interact with Ruby. For more details about this API, see the Modular GC API section.

Two GC implementations are included in Ruby:

  • Default: The GC implementation that is used by default in Ruby. This GC is stable and production ready. The implementation uses a mark-sweep-compact algorithm.
  • MMTk: An experimental implementation using the MMTk framework. The code lives in the ruby/mmtk repository and is synchronized here. MMTk provides a wide variety of GC algorithms to choose from. For usage instructions and current progress, refer to the ruby/mmtk repository.

Building guide

Tip

If you are not sure how to build Ruby, follow the Building Ruby guide.

Important

Ruby's modular GC feature is experimental and subject to change. There may be bugs or performance impacts. Use at your own risk.

Building Ruby with Modular GC

  1. Configure Ruby with the --with-modular-gc=<dir> option, where dir is the directory you want to place the built GC libraries into.
  2. Build Ruby as usual.

Building GC implementations shipped with Ruby

  1. Build your desired GC implementation with make install-modular-gc MODULAR_GC=<impl>. This will build the GC implementation and place the built library into the dir specified in step 1. impl can be one of:
    • default: The default GC that Ruby ships with.
    • mmtk: The GC that uses MMTk as the back-end. See Ruby-specific details in the ruby/mmtk repository.
  2. Run your desired GC implementation by setting the RUBY_GC_LIBRARY=<lib> environment variable, where lib could be default, mmtk, or your own implementation (as long as you place it in the dir specified in step 1).

Modular GC API

Warning

The Modular GC API is experimental and subject to change without notice.

GC implementations interact with Ruby via the Modular GC API. All implementations must provide the functions in gc/gc_impl.h for Ruby to hook into. GC implementations can use any public C API in Ruby, along with additional APIs defined in gc/gc.h.

Additionally, create an extconf.rb file to build the GC library. This file must use gc/extconf_base.rb and the create_gc_makefile method.