Commit graph

2647 commits

Author SHA1 Message Date
Satoshi Tagomori
294b52fb9b Follow the code style about else 2025-05-11 23:32:50 +09:00
Satoshi Tagomori
90e5ce6132 Rename RCLASS_EXT() macro to RCLASS_EXT_PRIME() to prevent using it wrongly
The macro RCLASS_EXT() accesses the prime classext directly, but it can be
valid only in a limited situation when namespace is enabled.
So, to prevent using RCLASS_EXT() in the wrong way, rename the macro and
let the developer check it is ok to access the prime classext or not.
2025-05-11 23:32:50 +09:00
Satoshi Tagomori
382645d440 namespace on read 2025-05-11 23:32:50 +09:00
Daisuke Aritomo
98667f82d2 [DOC] Update documentation for ObjectSpace#each_object
Co-authored-by: Benoit Daloze <eregontp@gmail.com>
2025-05-10 19:32:21 +02:00
Daisuke Aritomo
29b3d683fb [DOC] Make clear that current behavior is not ideal 2025-05-10 19:32:21 +02:00
Daisuke Aritomo
a51b4a86fc [DOC] ObjectSpace#each_object behavior in multi-Ractor mode
This behavior of ObjectSpace#each_object has been around since Ruby 3.0
when Ractors were first introduced, but was never documented and has
caused some amount of confusion:

https://bugs.ruby-lang.org/issues/17360
https://bugs.ruby-lang.org/issues/19387
https://bugs.ruby-lang.org/issues/21149
2025-05-10 19:32:21 +02:00
Jean Boussier
d9502a8386 Rename rb_field_get -> rb_obj_field_get
To be consistent with `rb_obj_field_set`.
2025-05-10 15:39:33 +02:00
Jean Boussier
3135eddb4e Refactor FIRST_T_OBJECT_SHAPE_ID to not be used outside shape.c 2025-05-09 20:45:48 +02:00
Jean Boussier
ea77250847 Rename RB_OBJ_SHAPE -> rb_obj_shape
As well as `RB_OBJ_SHAPE_ID` -> `rb_obj_shape_id`
and `RSHAPE` is now a simple alias for `rb_shape_lookup`.

I tried to turn all these into `static inline` but I'm having
trouble with `RUBY_EXTERN rb_shape_tree_t *rb_shape_tree_ptr;`
not being exposed as I'd expect.
2025-05-09 10:22:51 +02:00
Jean Boussier
becc45ff4e Eliminate some rb_shape_t * usages outside of shape.c. 2025-05-09 10:22:51 +02:00
Jean Boussier
5782561fc1 Rename rb_shape_get_shape_id -> RB_OBJ_SHAPE_ID
And `rb_shape_get_shape` -> `RB_OBJ_SHAPE`.
2025-05-09 10:22:51 +02:00
Jean Boussier
3f7c0af051 Rename rb_shape_obj_too_complex -> rb_shape_obj_too_complex_p 2025-05-09 10:22:51 +02:00
Jean Boussier
334ebba221 Rename rb_shape_get_shape_by_id -> RSHAPE 2025-05-09 10:22:51 +02:00
Jean Boussier
f8b3fc520f Refactor rb_shape_traverse_from_new_root to not expose rb_shape_t 2025-05-09 10:22:51 +02:00
Jean Boussier
4de049a3f9 Deprecate ObjectSpace._id2ref
[Feature #15408]

Matz decided to deprecate it for Ruby 2.6 or 2.7 but that never
actually happened.

Given the object_id table is a contention point for Ractors
it seems sensible to finally deprecate this API so we can
generate and store object ids more efficiently in the future.
2025-05-09 09:19:25 +02:00
Jean Boussier
cf9046c00b Refactor id_to_obj_tbl compaction
Use `st_foreach_with_replace` rather than to call `st_insert`
from inside `st_foreach`, this saves from having to disable GC.

Co-Authored-By: Peter Zhu <peter@peterzhu.ca>
2025-05-08 07:58:05 +02: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
6c9b3ac232 Refactor OBJ_TOO_COMPLEX_SHAPE_ID to not be referenced outside shape.h
Also refactor checks for `->type == SHAPE_OBJ_TOO_COMPLEX`.
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
Jeremy Evans
ce51ef30df Save one VALUE per embedded RTypedData
This halves the amount of memory used for embedded RTypedData if they
are one VALUE (8 bytes on 64-bit platforms) over the slot size limit.

For Set, on 64-bit it uses an embedded 56-byte struct.  With the
previous implementation, the embedded structs starts at offset 32,
resulting in a total size of 88.  Since that is over the 80 byte
limit, it goes to the next highest bucket, 160 bytes, wasting 72
bytes.  This allows it to fit in a 80 byte bucket, which reduces
the total size for small sets of from 224 bytes (160 bytes
embedded, 64 bytes malloc, 72 bytes wasted in embedding) to 144
bytes (80 bytes embedded, 64 bytes malloc, 0 bytes wasted in
embedding).

Any other embedded RTypedData will see similar advantages if they
are currently one VALUE over the limit.

To implement this, remove the typed_flag from struct RTypedData.
Embed the typed_flag information in the type member, which is
now a tagged pointer using VALUE type, using the bottom low 2 bits
as flags (1 bit for typed flag, the other for the embedded flag).
To get the actual pointer, RTYPEDDATA_TYPE masks out
the low 2 bits and then casts.  That moves the RTypedData data
pointer from offset 32 to offset 24 (on 64-bit).

Vast amount of code in the internals (and probably external C
extensions) expects the following code to work for both RData and
non-embedded RTypedData:

```c
DATA_PTR(obj) = some_pointer;
```

Allow this to work by moving the data pointer in RData between
the dmark and dfree pointers, so it is at the same offset (24
on 64-bit).

Other than these changes to the include files, the only changes
needed were to gc.c, to account for the new struct layouts,
handle setting the low bits in the type member, and to use
RTYPEDDATA_TYPE(obj) instead of RTYPEDDATA(obj)->type.
2025-05-05 09:46:32 +09:00
John Hawthorn
36c64b3be8 Also prefer FL_TEST_RAW in gc.c
Similar to 4a040eeb0d, I noticed the test
for FL_FINALIZE checking FL_ABLE in a profile, and we shouldn't need to
do that here.
2025-05-02 14:28:25 -07:00
Nobuyoshi Nakada
c218862d3c
Fix style [ci skip] 2025-04-19 22:02:10 +09:00
Samuel Williams
20a1c1dc6b
Ensure struct rb_io is passed through to thread.c. (#13134) 2025-04-19 09:55:16 +09:00
John Hawthorn
57b6a7503f Lock-free hash set for fstrings [Feature #21268]
This implements a hash set which is wait-free for lookup and lock-free
for insert (unless resizing) to use for fstring de-duplication.

As highlighted in https://bugs.ruby-lang.org/issues/19288, heavy use of
fstrings (frozen interned strings) can significantly reduce the
parallelism of Ractors.

I tried a few other approaches first: using an RWLock, striping a series
of RWlocks (partitioning the hash N-ways to reduce lock contention), and
putting a cache in front of it. All of these improved the situation, but
were unsatisfying as all still required locks for writes (and granular
locks are awkward, since we run the risk of needing to reach a vm
barrier) and this table is somewhat write-heavy.

My main reference for this was Cliff Click's talk on a lock free
hash-table for java https://www.youtube.com/watch?v=HJ-719EGIts. It
turns out this lock-free hash set is made easier to implement by a few
properties:

 * We only need a hash set rather than a hash table (we only need keys,
   not values), and so the full entry can be written as a single VALUE
 * As a set we only need lookup/insert/delete, no update
 * Delete is only run inside GC so does not need to be atomic (It could
   be made concurrent)
 * I use rb_vm_barrier for the (rare) table rebuilds (It could be made
   concurrent) We VM lock (but don't require other threads to stop) for
   table rebuilds, as those are rare
 * The conservative garbage collector makes deferred replication easy,
   using a T_DATA object

Another benefits of having a table specific to fstrings is that we
compare by value on lookup/insert, but by identity on delete, as we only
want to remove the exact string which is being freed. This is faster and
provides a second way to avoid the race condition in
https://bugs.ruby-lang.org/issues/21172.

This is a pretty standard open-addressing hash table with quadratic
probing. Similar to our existing st_table or id_table. Deletes (which
happen on GC) replace existing keys with a tombstone, which is the only
type of update which can occur. Tombstones are only cleared out on
resize.

Unlike st_table, the VALUEs are stored in the hash table itself
(st_table's bins) rather than as a compact index. This avoids an extra
pointer dereference and is possible because we don't need to preserve
insertion order. The table targets a load factor of 2 (it is enlarged
once it is half full).
2025-04-18 13:03:54 +09:00
John Hawthorn
89199a47db Extract rb_gc_free_fstring to string.c
This allows more flexibility in how we deal with the fstring table
2025-04-18 13:03:54 +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
Jean Boussier
085cc6e434 Ractor: revert to moving object bytes, but size pool aware
Using `rb_obj_clone` introduce other problems, such as `initialize_*`
callbacks invocation in the context of the parent ractor.

So we can revert back to copy the content of the object slots,
but in a way that is aware of size pools.
2025-04-04 16:26:29 +02:00
Jean Boussier
7db0e07134 Don't preserve object_id when moving object to another Ractor
That seemed like the logical thing to do to me, but ko1 disagree.
2025-03-31 12:01:55 +02:00
Jean Boussier
0350290262 Ractor: Fix moving embedded objects
[Bug #20271]
[Bug #20267]
[Bug #20255]

`rb_obj_alloc(RBASIC_CLASS(obj))` will always allocate from the basic
40B pool, so if `obj` is larger than `40B`, we'll create a corrupted
object when we later copy the shape_id.

Instead we can use the same logic than ractor copy, which is
to use `rb_obj_clone`, and later ask the GC to free the original
object.

We then must turn it into a `T_OBJECT`, because otherwise
just changing its class to `RactorMoved` leaves a lot of
ways to keep using the object, e.g.:

```
a = [1, 2, 3]
Ractor.new{}.send(a, move: true)
[].concat(a) # Should raise, but wasn't.
```

If it turns out that `rb_obj_clone` isn't performant enough
for some uses, we can always have carefully crafted specialized
paths for the types that would benefit from it.
2025-03-31 12:01:55 +02:00
Peter Zhu
2183899fd1 Re-use objspace variable instead of calling rb_gc_get_objspace() 2025-03-26 09:35:51 -04:00
Peter Zhu
319fcca656 Move rb_gc_impl_ractor_cache_free to shutdown section 2025-03-24 08:49:30 -04:00
Peter Zhu
a572ec1ba0 Move rb_gc_impl_objspace_free to shutdown section 2025-03-24 08:49:30 -04:00
Peter Zhu
7b6e07ea93 Add rb_gc_object_metadata API
This function replaces the internal rb_obj_gc_flags API. rb_gc_object_metadata
returns an array of name and value pairs, with the last element having
0 for the name.
2025-02-19 09:47:28 -05:00
Peter Zhu
0597cbcb1d Fix crash for special constants in too complex generic ivars
We should skip reference updating for entries in too complex generic ivars
that are special constants. This fixes the following crash:

    MAX_SHAPES = 0x80000

    MAX_SHAPES.times do |i|
      o = []
      o.instance_variable_set(:"@foo#{i}", 1)
    end

    o = []

    o.instance_variable_set(:"@a", 123)

    GC.compact
2025-02-18 17:09:28 -05:00
Nobuyoshi Nakada
4a67ef09cc
[Feature #21116] Extract RJIT as a third-party gem 2025-02-13 18:01:03 +09:00
Peter Zhu
3fb455adab Move global symbol reference updating to rb_sym_global_symbols_update_references 2025-02-10 08:47:44 -05:00
Peter Zhu
8d0416ae0b Make ruby_global_symbols movable
The `ids` array and `dsymbol_fstr_hash` were pinned because they were
kept alive by rb_vm_register_global_object. This prevented the GC from
moving them even though there were reference updating code.

This commit changes it to be marked movable by marking it as a root object.
2025-02-10 08:47:44 -05:00
Peter Zhu
a084fef9af [Bug #21099] Fix GC when Ractor list not initialized
When the Ractor list is not initialized and a GC is ran at boot, then it
would crash because the newobj_cache of the main Ractor is not cleared.
This commit changes it to use ruby_single_main_ractor when it's available
and iterate over the Ractor list when we have multiple Ractors.
2025-01-30 10:10:48 -05:00
Peter Zhu
98b36f6f36 Use rb_gc_vm_weak_table_foreach for reference updating
We can use rb_gc_vm_weak_table_foreach for reference updating of weak tables
in the default GC.
2025-01-27 10:28:36 -05:00
Peter Zhu
9e5ff79c5b Optionally traverse non-weak references in rb_gc_vm_weak_table_foreach
For moving garbage collectors, we may want to combine liveliness checking
with reference updating for performance. This commit allows for non-weak
references to be passed into the callback function when weak_only is false.
2025-01-27 10:28:36 -05:00
Peter Zhu
7ed08c4fd3 Fix memory leak in rb_gc_vm_weak_table_foreach
When deleting from the generic ivar table, we need to free the gen_ivtbl
otherwise we will have a memory leak.
2025-01-23 10:24:35 -05:00
Peter Zhu
89240eb2fb Add generic ivar reference updating step
Previously, generic ivars worked differently than the other global tables
during compaction. The other global tables had their references updated
through iteration during rb_gc_update_vm_references. Generic ivars updated
the keys when the object moved and updated the values while reference
updating the object. This is inefficient as this required one lookup for
every moved object and one lookup for every object with generic ivars.

Instead, this commit changes it to iterate over the generic ivar table to
update both the keys and values.
2025-01-22 08:54:52 -05:00
Peter Zhu
d78aef5e3f Add not null checks to rb_gc_vm_weak_table_foreach
If the tables are null (which happens when a GC is ran at boot), it will
segfault when trying to iterate.
2025-01-16 10:31:47 -05:00
Nobuyoshi Nakada
5df20ab0b4
Un-constify mark_current_machine_context on wasm
As `SET_STACK_END` updates `ec->machine.stack_end`, it cannot be
const.
2025-01-16 22:22:43 +09:00
Peter Zhu
67744879a1 Use existing vm variable for frozen strings in rb_gc_vm_weak_table_foreach 2025-01-15 15:57:52 -05:00
Peter Zhu
99ff0224a5 Move rbimpl_size_add_overflow from gc.c to memory.h 2025-01-02 11:03:04 -05:00
Nobuyoshi Nakada
7df5d65eac
[Bug #20981] Bring back rb_undefine_finalizer 2024-12-25 22:21:37 +09:00
Peter Zhu
f4476f0d07 Disable GC during RUBY_INTERNAL_EVENT_NEWOBJ
We must disable GC when running RUBY_INTERNAL_EVENT_NEWOBJ hooks because
the callback could call xmalloc which could potentially trigger a GC,
and a lot of code is unsafe to trigger a GC right after an object has
been allocated because they perform initialization for the object and
assume that the GC does not trigger before then.
2024-12-23 09:03:32 -05:00
Nobuyoshi Nakada
2f2530b195
Allow variables in modular_gc_dir
Such as `$(ruby_version)`, `$(arch)` and so on.
2024-12-22 22:10:26 +09:00