225 Commits

Author SHA1 Message Date
Peter Zhu
a4948c30fd Add debug message to assertion for checking GC mode
We assert that the GC is not in a cycle in gc_start, but it does not show
what phase we're in if the assertion fails. This commit adds a debug
message for when the assertion fails.
2025-06-26 09:37:55 -04:00
Peter Zhu
80f53eba40 Support message in GC_ASSERT
RUBY_ASSERT_MESG_WHEN supports a format string at the end for additional
information. This commit adds support for that in GC_ASSERT.
2025-06-26 09:37:55 -04:00
Nobuyoshi Nakada
dbc596938a
Move a comment to the corresponding conditional block [ci skip] 2025-06-21 13:14:42 +09:00
Jean Boussier
545e99da66 mmtk: Get rid of unused reference to FL_EXIVAR 2025-06-13 17:13:08 +02:00
Peter Zhu
6184793ea9 [DOC] Split building docs for modular GC 2025-06-09 22:30:43 -04:00
Peter Zhu
837699e160 Take file and line in GC VM locks
This commit adds file and line to GC VM locking functions for debugging
purposes and adds upper case macros to pass __FILE__ and __LINE__.
2025-06-09 13:57:18 -04:00
alpaca-tc
c8ddc0a843 Optimize callcache invalidation for refinements
Fixes [Bug #21201]

This change addresses a performance regression where defining methods
inside `refine` blocks caused severe slowdowns. The issue was due to
`rb_clear_all_refinement_method_cache()` triggering a full object
space scan via `rb_objspace_each_objects` to find and invalidate
affected callcaches, which is very inefficient.

To fix this, I introduce `vm->cc_refinement_table` to track
callcaches related to refinements. This allows us to invalidate
only the necessary callcaches without scanning the entire heap,
resulting in significant performance improvement.
2025-06-09 12:33:35 +09:00
Peter Zhu
347e581a4c Introduce MODULAR_GC_FN
MODULAR_GC_FN allows functions in gc.c to be defined as static when not
building with modular GC.
2025-06-06 10:46:55 -04:00
Peter Zhu
ea8b53a539 Allow pass special constants to the write barrier
Some GC implementations want to always know when an object is written to,
even if the written value is a special constant. Checking special constants
in rb_obj_written was a micro-optimization that made assumptions about
the GC implementation.
2025-06-03 12:04:24 -04:00
Peter Zhu
acc273a8a4 Remove dependancy of default.c on internal/object.h
We don't want the default GC to depend on Ruby internals so we can build
it as a modular GC.
2025-06-02 09:52:25 -04:00
Koichi Sasada
ef2bb61018 Ractor::Port
* Added `Ractor::Port`
  * `Ractor::Port#receive` (support multi-threads)
  * `Rcator::Port#close`
  * `Ractor::Port#closed?`
* Added some methods
  * `Ractor#join`
  * `Ractor#value`
  * `Ractor#monitor`
  * `Ractor#unmonitor`
* Removed some methods
  * `Ractor#take`
  * `Ractor.yield`
* Change the spec
  * `Racotr.select`

You can wait for multiple sequences of messages with `Ractor::Port`.

```ruby
ports = 3.times.map{ Ractor::Port.new }
ports.map.with_index do |port, ri|
  Ractor.new port,ri do |port, ri|
    3.times{|i| port << "r#{ri}-#{i}"}
  end
end

p ports.each{|port| pp 3.times.map{port.receive}}

```

In this example, we use 3 ports, and 3 Ractors send messages to them respectively.
We can receive a series of messages from each port.

You can use `Ractor#value` to get the last value of a Ractor's block:

```ruby
result = Ractor.new do
  heavy_task()
end.value
```

You can wait for the termination of a Ractor with `Ractor#join` like this:

```ruby
Ractor.new do
  some_task()
end.join
```

`#value` and `#join` are similar to `Thread#value` and `Thread#join`.

To implement `#join`, `Ractor#monitor` (and `Ractor#unmonitor`) is introduced.

This commit changes `Ractor.select()` method.
It now only accepts ports or Ractors, and returns when a port receives a message or a Ractor terminates.

We removes `Ractor.yield` and `Ractor#take` because:
* `Ractor::Port` supports most of similar use cases in a simpler manner.
* Removing them significantly simplifies the code.

We also change the internal thread scheduler code (thread_pthread.c):
* During barrier synchronization, we keep the `ractor_sched` lock to avoid deadlocks.
  This lock is released by `rb_ractor_sched_barrier_end()`
  which is called at the end of operations that require the barrier.
* fix potential deadlock issues by checking interrupts just before setting UBF.

https://bugs.ruby-lang.org/issues/21262
2025-05-31 04:01:33 +09:00
Kunshan Wang
d2a1ad00cb [ruby/mmtk] Fix environment variable parsing
Ues more idiomatic rust approaches.

https://github.com/ruby/mmtk/commit/ef125f9eae
2025-05-30 14:55:42 +00:00
Kunshan Wang
94688bdc7d [ruby/mmtk] Fix clippy warnings and formatting.
We also enable `#![warn(unsafe_op_in_unsafe_fn)]` in the whole mmtk_ruby
crate.

https://github.com/ruby/mmtk/commit/8b8025f71a
2025-05-30 14:55:42 +00:00
Kunshan Wang
d8774ec98f [ruby/mmtk] Bump MMTk and dependencies version
https://github.com/ruby/mmtk/commit/de252637ec
2025-05-30 14:55:41 +00:00
Kunshan Wang
60de513d05 [ruby/mmtk] Remove unused constant
Remove the unused constant HAS_MOVED_GFIELDSTBL and related methods.

In the mmtk/mmtk-ruby repo, we are now able to find the global field
(IV) table of a moved object during copying GC without using the
HAS_MOVED_GFIELDSTBL bit.  We synchronize some of the code, although we
haven't implemented moving GC in ruby/mmtk, yet.

See: 13080acdf5

https://github.com/ruby/mmtk/commit/400ba4e747
2025-05-30 14:55:41 +00:00
Peter Zhu
38ecaca155 [ruby/mmtk] Remove dependance on internal/object.h
https://github.com/ruby/mmtk/commit/fdc13963f0
2025-05-29 14:46:49 +00:00
John Hawthorn
f483befd90 Add shape_id to RBasic under 32 bit
This makes `RBobject` `4B` larger on 32 bit systems
but simplifies the implementation a lot.

[Feature #21353]

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2025-05-26 10:31:54 +02:00
Peter Zhu
e00c46017b Drop unnecessary compiler guards for memory_sanitizer
We unpoison slots allocated out of the GC, so we don't need to disable
the assertions that read from the memory.
2025-05-23 14:35:56 -04:00
Peter Zhu
9130023cf5 Remove dependency on bits.h in default.c when BUILDING_MODULAR_GC
We can assume that the compiler will have __builtin_clzll so we can implement
nlz_int64 using that.
2025-05-23 14:35:56 -04:00
Peter Zhu
3487117e55 [ruby/mmtk] Fix object ID in rb_gc_impl_define_finalizer
The 0th element of the finalizer table array should be the object ID.

https://github.com/ruby/mmtk/commit/75e4a82652
2025-05-21 14:35:36 +00:00
Peter Zhu
27b0638386 [ruby/mmtk] Fix object ID for finalizers
We should get the object ID for finalizers in rb_gc_impl_define_finalizer
instead of when we create the finalizer job in make_final_job because
when we are in multi-Ractor mode, object ID needs to walk the references
which allocates an identity hash table. We cannot allocate in make_final_job
because it is in a MMTk worker thread.

https://github.com/ruby/mmtk/commit/922f22a690
2025-05-21 02:30:33 +00:00
Peter Zhu
b043abc048 Only define RVALUE_OVERHEAD if undefined
This allows RVALUE_OVERHEAD to be defined elsewhere.
2025-05-20 12:57:23 -04:00
Jean Boussier
1e33a451bb gc: Execute run_final with the lock held
The finalizer table can't be read nor modified without the VM lock.
2025-05-16 20:16:52 +02:00
Jean Boussier
ec8900e3eb rb_gc_impl_copy_finalizer: generate a new object id
Fix a regression introduced by: https://github.com/ruby/ruby/pull/13155
2025-05-16 20:16:52 +02:00
Jean Boussier
a294427017 Add missing lock to rb_gc_impl_copy_finalizer 2025-05-16 20:16:52 +02:00
Peter Zhu
04f538c144 Remove dependency on sanitizers.h in default.c when BUILDING_MODULAR_GC 2025-05-15 14:13:53 -04:00
Jean Boussier
ed632cd0ba Add missing lock in rb_gc_impl_undefine_finalizer
The table is global so accesses must be synchronized.
2025-05-15 13:32:08 +02:00
Jean Boussier
3d1b8e7298 newobj_fill: don't assume RBasic size
The previous implementation assumed `RBasic` size is `2 * sizeof(VALUE)`,
might as well not make assumption and use a proper `sizeof`.

Co-Authored-By: John Hawthorn <john@hawthorn.email>
2025-05-15 13:26:26 +02:00
Alan Wu
92b218fbc3 YJIT: ZJIT: Allow both JITs in the same build
This commit allows building YJIT and ZJIT simultaneously, a "combo
build". Previously, `./configure --enable-yjit --enable-zjit` failed. At
runtime, though, only one of the two can be enabled at a time.

Add a root Cargo workspace that contains both the yjit and zjit crate.
The common Rust build integration mechanisms are factored out into
defs/jit.mk.

Combo YJIT+ZJIT dev builds are supported; if either JIT uses
`--enable-*=dev`, both of them are built in dev mode.

The combo build requires Cargo, but building one JIT at a time with only
rustc in release build remains supported.
2025-05-15 00:39:03 +09:00
Jean Boussier
f9c3feccf4 Rename id_to_obj_tbl -> id2ref_tbl
As well as associated functions, this should make it more obvious
what the purpose is.
2025-05-14 11:41:14 +02:00
John Hawthorn
3306d7d2f9 Only clear Ractor cache in child
This avoids a race condition where we were clearing the cache from
another ractor while it was in use. Oops!

Fixes this failure http://ci.rvm.jp/results/master@oci-aarch64/5750416
2025-05-09 16:15:54 -07:00
Jean Boussier
15e3675ecc Remove dead code in rb_gc_impl_ractor_cache_free
Followup: https://github.com/ruby/ruby/pull/13286
2025-05-09 11:13:52 +02:00
John Hawthorn
30ef0f180b Fix allocation count when forking with Ractors
After fork we reset to single ractor mode (which IMO we shouldn't do,
but it requires more work to fix) and so we need to add the pending
object counts back to the main heap.
2025-05-09 00:55:14 -07:00
Peter Zhu
c18bedcdbb Remove dependency on debug_counter.h when BUILDING_MODULAR_GC
This allows the default GC to not need debug_counter.h when building as a
modular GC.
2025-05-08 10:36:27 -04:00
Peter Zhu
3f5080e767 Stop checking for USE_DEBUG_COUNTER in default.c
We don't need to check for USE_DEBUG_COUNTER because the code is no-op
if USE_DEBUG_COUNTER is not enabled.
2025-05-08 10:36:27 -04:00
Jean Boussier
2d1241ba97 Get rid of RB_GC_VM_ID_TO_OBJ_TABLE_KEYS 2025-05-08 07:58:05 +02:00
Jean Boussier
f48e45d1e9 Move object_id in object fields.
And get rid of the `obj_to_id_tbl`

It's no longer needed, the `object_id` is now stored inline
in the object alongside instance variables.

We still need the inverse table in case `_id2ref` is invoked, but
we lazily build it by walking the heap if that happens.

The `object_id` concern is also no longer a GC implementation
concern, but a generic implementation.

Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>
2025-05-08 07:58:05 +02:00
Jean Boussier
0ea210d1ea Rename ivptr -> fields, next_iv_index -> next_field_index
Ivars will longer be the only thing stored inline
via shapes, so keeping the `iv_index` and `ivptr` names
would be confusing.

Instance variables won't be the only thing stored inline
via shapes, so keeping the `ivptr` name would be confusing.

`field` encompass anything that can be stored in a VALUE array.

Similarly, `gen_ivtbl` becomes `gen_fields_tbl`.
2025-05-08 07:58:05 +02:00
Peter Zhu
3e94b5f9c0 Remove dependence on internal/hash.h for default GC 2025-05-07 11:47:13 -04:00
Étienne Barrié
cb772247e7 Improve correctness contention for allocated object counts
Currently the count of allocated object for a heap is incremented
without regards to parallelism which leads to incorrect counts.

By maintaining a local counter in the ractor newobj cache, and only
syncing atomically with some granularity, we can improve the correctness
without increasing contention.

The allocated object count is also synced when the ractor is freed.

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2025-05-06 19:13:59 +02:00
Nobuyoshi Nakada
c772d2691d
Count metadata entries automatically from the names list 2025-04-25 19:40:04 +09:00
Jean Boussier
62a7f17157 Update RB_GC_OBJECT_METADATA_ENTRY_COUNT
In cb1ea54bbf6cdf49c53f42720fec1a151069810c I added one more
metadata flag, but didn't notice `RB_GC_OBJECT_METADATA_ENTRY_COUNT`
had to be incremented.

This should fix ASAN builds.

Interestingly, bdb25959fb047af0358f33d7327b7752dca14aa4 already
caused the count to be off by one, so I had to increment it by
2.
2025-04-25 11:17:31 +02:00
Jean Boussier
cb1ea54bbf objspace_dump: Include shareable flag
Given that the currently planned ractor local GC implementation
performance will heavilly be influenced by the number of shareable
objects it would be valuable to be able to know how many of them
are in the heap.
2025-04-24 10:14:29 +02:00
Jean Boussier
1048b162de Eagerly store a copy of object_id in finalizer table.
This makes the finalizer table fully self contained, so GC no
longer need to delay cleaning the `obj_to_id_tbl`.
2025-04-23 11:54:29 +02:00
Jean Boussier
6d0dd7d863 rb_gc_impl_define_finalizer: unlock on early return 2025-04-23 07:04:41 +02:00
Jean Boussier
7c30bd50df Add missing lock in rb_gc_impl_define_finalizer
`objspace->finalizer_table` must be synchronized,
otherwise concurrent insertion from multiple ractors
will cause a crash.

Repro:

```ruby
ractors = 5.times.map do |i|
  Ractor.new do
    100_000.times.map do
      o = Object.new
      ObjectSpace.define_finalizer(o, ->(id) {})
      o
    end
  end
end

ractors.each(&:take)
```
2025-04-22 23:23:35 +02:00
dependabot[bot]
f6fddbd337 Bump crossbeam-channel from 0.5.13 to 0.5.15 in /gc/mmtk
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.13 to 0.5.15.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.13...crossbeam-channel-0.5.15)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-version: 0.5.15
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-15 17:14:26 +09:00
Jean Boussier
0606046c1a Lazily create objspace->id_to_obj_tbl
This inverse table is only useful if `ObjectSpace._id2ref` is used,
which is extremely rare. The only notable exception is the `drb` gem
and even then it has an option not to rely on `_id2ref`.

So if we assume this table will never be looked up, we can just
not maintain it, and if it turns out `_id2ref` is called, we
can lock the VM and re-build it.

```
compare-ruby: ruby 3.5.0dev (2025-04-10T09:44:40Z master 684cfa42d7) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-04-10T10:13:43Z lazy-id-to-obj d3aa9626cc) +YJIT +PRISM [arm64-darwin24]
warming up..

|           |compare-ruby|built-ruby|
|:----------|-----------:|---------:|
|baseline   |     26.364M|   25.974M|
|           |       1.01x|         -|
|object_id  |     10.293M|   14.202M|
|           |           -|     1.38x|
```
2025-04-15 07:57:39 +09:00
Kunshan Wang
d17ab5a430 [ruby/mmtk] Do root scanning in scan_vm_specific_roots
We rely on scan_vm_specific_roots to reach all stacks via the following
path:

    VM -> ractors -> threads -> fibers -> stacks

https://github.com/ruby/mmtk/commit/0a6a835aaa
2025-04-08 03:12:50 +00:00
Peter Zhu
d4406f0627 Grow GC heaps independently
[Bug #21214]

If we allocate objects where one heap holds transient objects and another
holds long lived objects, then the heap with transient objects will grow
along the heap with long lived objects, causing higher memory usage.

For example, we can see this issue in this script:

    def allocate_small_object = []
    def allocate_large_object = Array.new(10)

    arys = Array.new(1_000_000) do
      # Allocate 10 small transient objects
      10.times { allocate_small_object }
      # Allocate 1 large object that is persistent
      allocate_large_object
    end

    pp GC.stat
    pp GC.stat_heap

Before this change:

    heap_live_slots: 2837243
    {0 =>
      {slot_size: 40,
       heap_eden_pages: 1123,
       heap_eden_slots: 1838807},
     2 =>
      {slot_size: 160,
       heap_eden_pages: 2449,
       heap_eden_slots: 1001149},
    }

After this change:

    heap_live_slots: 1094474
    {0 =>
      {slot_size: 40,
       heap_eden_pages: 58,
       heap_eden_slots: 94973},
     2 =>
      {slot_size: 160,
       heap_eden_pages: 2449,
       heap_eden_slots: 1001149},
    }
2025-04-07 09:41:11 -04:00