228 Commits
Author SHA1 Message Date
John Hawthorn 3c6a6afa1c [DOC] Update ractor.c docs 2025-12-18 15:59:49 -08:00
Luke Gruber 4fb537b1ee Make tracepoints with set_trace_func or TracePoint.new ractor local (#15468)
Before this change, GC'ing any Ractor object caused you to lose all
enabled tracepoints across all ractors (even main). Now tracepoints are
ractor-local and this doesn't happen. Internal events are still global.

Fixes [Bug #19112]
2025-12-16 14:06:55 -05:00
Jean Boussier e42bcd7ce7 Rename fiber_serial into ec_serial
Since it now live in the EC.
2025-12-16 09:51:07 +01:00
John Hawthorn 90a9b64238 Use rb_current_ec_noinline in ractor_{lock,unlock}
We're seeing an occasional crash on CI because this ends up inlined all
the way into ractor_wait_receive. On llvm (possibly other compilers) the
thread local address of ec ends up cached (not the value of ec, the
address ec is read from). So if we are migrated to another native
thread, that may be invalid.

Using rb_current_ec_noinline avoids this problems. It would be good to
adjust this code so that ec (or current ractor) is calculated once and
then passed through to both lock and unlock.
2025-12-08 11:48:20 -08:00
Koichi Sasada 02cddcc2be Ractor.shareable_proc(&pr) should copy pr
`pr` should not change on this method.
2025-12-05 10:45:03 +09:00
Koichi Sasada 2aaea665bb fix typo s/sharable/shareable/ 2025-12-05 03:10:50 +09:00
Koichi Sasada f2cd772329 (experimental) RUBY_TYPED_FROZEN_SHAREABLE_NO_REC
`T_DATA` has a flag `RUBY_TYPED_FROZEN_SHAREABLE` which means
if the `T_DATA` object is frozen, it can be sharable.
On the `Ractor.make_sharable(obj)`, rechable objects from the
`T_DATA` object will be apply `Ractor.make_shareable` recursively.

`RUBY_TYPED_FROZEN_SHAREABLE_NO_REC` is similar to the
`RUBY_TYPED_FROZEN_SHAREABLE`, but doesn't apply `Ractor.make_sharable`
recursively for children.
If it refers to unshareable objects, it will simply raise an error.

I'm not sure this pattern is common or not, so it is not in public.
If we find more cases, we can discuss publication.
2025-12-05 02:28:30 +09:00
Jean Boussier 5770c186d1 Rename rb_obj_exivar_p -> rb_obj_gen_fields_p
The "EXIVAR" terminology has been replaced by "gen fields"
AKA "generic fields".

Exivar implies variable, but generic fields include more than
just variables, e.g. `object_id`.
2025-12-03 15:57:26 +01:00
Luke Gruber bee02c41bc Fix a ractor barrier issue during VM cleanup. (#15091) 2025-11-26 17:14:55 -05:00
Luke Gruber 1660b8145c Eliminate redundant work and branching when marking T_OBJECT (#15274) 2025-11-26 16:23:34 -05:00
John Hawthorn 4263f1d718 Store fiber serial as Ractor-local 2025-11-25 13:48:35 -08:00
Luke Gruber 54907db8f3 Fix ractor move of object with generic ivars (#15056)
This bug was happening only when the `id2ref` table exists. We need
to replace the generic fields before replacing the object id of the
newly moved object.

Fixes [Bug #21664]
2025-11-05 12:42:08 -05:00
Koichi Sasada a003903e62 fix cross ractor require
`cross_ractor_require` is sharable object but it refers
to unsharable objects.

To fix it, make the process simple.
2025-10-30 18:04:08 +09:00
Koichi Sasada 09e9247edc MatchData may refer a String 2025-10-30 18:04:08 +09:00
Koichi Sasada b09f782fee Ractor's queue can contain unshareable objects 2025-10-30 18:04:08 +09:00
Koichi Sasada 8e4dc1099a Ractor's name should be shareable 2025-10-30 18:04:08 +09:00
Joshua Young 52ea222027 Fix segfault when moving nested objects between ractors during GC
Fixes a segmentation fault when moving nested objects between ractors with GC stress enabled and YJIT.

The issue is a timing problem: `move_enter` allocates new object shells but leaves their contents uninitialized until `move_leave` copies the actual data. If GC runs between these steps (which GC stress makes likely), it tries to follow what appear to be object pointers but are actually uninitialized memory, encountering null or invalid addresses.

The fix zero-initializes the object contents immediately after allocation in `move_enter`, ensuring the GC finds safe null pointers instead of garbage data.

The crash reproduced most consistently with nested hashes and YJIT, likely because nested structures create multiple uninitialized objects simultaneously while YJIT's memory usage increases the probability of GC triggering during moves.
2025-10-27 07:59:43 +09:00
Koichi Sasada bc00c4468e use SET_SHAREABLE
to adopt strict shareable rule.

* (basically) shareable objects only refer shareable objects
* (exception) shareable objects can refere unshareable objects
  but should not leak reference to unshareable objects to Ruby world
2025-10-23 13:08:26 +09:00
Koichi Sasada 45907b1b00 add SET_SHAREABLE macros
* `RB_OBJ_SET_SHAREABLE(obj)` makes obj shareable.
  All of reachable objects from `obj` should be shareable.
* `RB_OBJ_SET_FROZEN_SHAREABLE(obj)` same as above
  but freeze `obj` before making it shareable.

Also `rb_gc_verify_shareable(obj)` is introduced to check
the `obj` does not violate shareable rule (an shareable object
only refers shareable objects) strictly.

The rule has some exceptions (some shareable objects can refer to
unshareable objects, such as a Ractor object (which is a shareable
object) can refer to the Ractor local objects.
To handle such case, `check_shareable` flag is also introduced.

`STRICT_VERIFY_SHAREABLE` macro is also introduced to verify
the strict shareable rule at `SET_SHAREABLE`.
2025-10-23 13:08:26 +09:00
Nobuyoshi Nakada e94a2f691d [Bug #21638] Mark ractor-local $VERBOSE and $DEBUG
https://github.com/sampersand/blog/blob/master/the%20-s%20flag.md#the-segfault
2025-10-14 12:38:52 +09:00
John Hawthorn 272e5b7cc5 [DOC] Remove now inaccurate comment about blocking
Originally ractor_next_id used a VM_LOCK, but now it is an atomic and
won't block.
2025-10-02 11:06:35 -07:00
Koichi Sasada 55b1ba3bf2 Ractor.shareable_proc
call-seq:
  Ractor.sharable_proc(self: nil){} -> sharable proc

It returns shareable Proc object. The Proc object is
shareable and the self in a block will be replaced with
the value passed via `self:` keyword.

In a shareable Proc, the outer variables should
* (1) refer shareable objects
* (2) be not be overwritten

```ruby
  a = 42
  Ractor.shareable_proc{ p a }
  #=> OK

  b = 43
  Ractor.shareable_proc{ p b; b = 44 }
  #=> Ractor::IsolationError because 'b' is reassigned in the block.

  c = 44
  Ractor.shareable_proc{ p c }
  #=> Ractor::IsolationError because 'c' will be reassigned outside of the block.
  c = 45

  d = 45
  d = 46 if cond
  Ractor.shareable_proc{ p d }
  #=> Ractor::IsolationError because 'd' was reassigned outside of the block.
```

The last `d`'s case can be relaxed in a future version.

The above check will be done in a static analysis at compile time,
so the reflection feature such as `Binding#local_varaible_set`
can not be detected.

```ruby
  e = 42
  shpr = Ractor.shareable_proc{ p e } #=> OK
  binding.local_variable_set(:e, 43)
  shpr.call #=> 42 (returns captured timing value)
```

Ractor.sharaeble_lambda is also introduced.
[Feature #21550]
[Feature #21557]
2025-09-24 03:59:03 +09:00
Peter Zhu 4e05fe54e2 Use union in cross_ractor_require
A cross_ractor_require is either a require or an autoload, so we can make
the fields an union. This reduces the size of cross_ractor_require by 8
bytes.
2025-09-18 09:25:41 -04:00
Peter Zhu 8935cf98e5 Fix malloc_gc_disabled in Ractor lock
We should disable GC for malloc for the current Ractor instead of the locked
Ractor because it's the current Ractor that could run code that mallocs.
2025-08-27 09:26:52 -04:00
Jean Boussier 5257e1298c Replace ROBJECT_EMBED by ROBJECT_HEAP
The embed layout is way more common than the heap one,
especially since WVA.

I think it makes for more readable code to inverse the
flag.
2025-08-27 12:41:07 +02:00
Peter Zhu fca258f97f Fix deadlock when malloc in Ractor lock
If we malloc when the current Ractor is locked, we can deadlock because
GC requires VM lock and Ractor barrier. If another Ractor is waiting on
this Ractor lock, then it will deadlock because the other Ractor will
never join the barrier.

For example, this script deadlocks:

    r = Ractor.new do
      loop do
        Ractor::Port.new
      end
    end

    100000.times do |i|
      r.send(nil)
      puts i
    end

On debug builds, it fails with this assertion error:

    vm_sync.c:75: Assertion Failed: vm_lock_enter:cr->sync.locked_by != rb_ractor_self(cr)

On non-debug builds, we can see that it deadlocks in the debugger:

    Main Ractor:
    frame #3: 0x000000010021fdc4 miniruby`rb_native_mutex_lock(lock=<unavailable>) at thread_pthread.c:115:14
    frame #4: 0x0000000100193eb8 miniruby`ractor_send0 [inlined] ractor_lock(r=<unavailable>, file=<unavailable>, line=1180) at ractor.c:73:5
    frame #5: 0x0000000100193eb0 miniruby`ractor_send0 [inlined] ractor_send_basket(ec=<unavailable>, rp=0x0000000131092840, b=0x000000011c63de80, raise_on_error=true) at ractor_sync.c:1180:5
    frame #6: 0x0000000100193eac miniruby`ractor_send0(ec=<unavailable>, rp=0x0000000131092840, obj=4, move=<unavailable>, raise_on_error=true) at ractor_sync.c:1211:5

    Second Ractor:
    frame #2: 0x00000001002208d0 miniruby`rb_ractor_sched_barrier_start [inlined] rb_native_cond_wait(cond=<unavailable>, mutex=<unavailable>) at thread_pthread.c:221:13
    frame #3: 0x00000001002208cc miniruby`rb_ractor_sched_barrier_start(vm=0x000000013180d600, cr=0x0000000131093460) at thread_pthread.c:1438:13
    frame #4: 0x000000010028a328 miniruby`rb_vm_barrier at vm_sync.c:262:13 [artificial]
    frame #5: 0x00000001000dfa6c miniruby`gc_start [inlined] rb_gc_vm_barrier at gc.c:179:5
    frame #6: 0x00000001000dfa68 miniruby`gc_start [inlined] gc_enter(objspace=0x000000013180fc00, event=gc_enter_event_start, lock_lev=<unavailable>) at default.c:6636:9
    frame #7: 0x00000001000dfa48 miniruby`gc_start(objspace=0x000000013180fc00, reason=<unavailable>) at default.c:6361:5
    frame #8: 0x00000001000e3fd8 miniruby`objspace_malloc_increase_body [inlined] garbage_collect(objspace=0x000000013180fc00, reason=512) at default.c:6341:15
    frame #9: 0x00000001000e3fa4 miniruby`objspace_malloc_increase_body [inlined] garbage_collect_with_gvl(objspace=0x000000013180fc00, reason=512) at default.c:6741:16
    frame #10: 0x00000001000e3f88 miniruby`objspace_malloc_increase_body(objspace=0x000000013180fc00, mem=<unavailable>, new_size=<unavailable>, old_size=<unavailable>, type=<unavailable>) at default.c:8007:13
    frame #11: 0x00000001000e3c44 miniruby`rb_gc_impl_malloc [inlined] objspace_malloc_fixup(objspace=0x000000013180fc00, mem=0x000000011c700000, size=12582912) at default.c:8085:5
    frame #12: 0x00000001000e3c30 miniruby`rb_gc_impl_malloc(objspace_ptr=0x000000013180fc00, size=12582912) at default.c:8182:12
    frame #13: 0x00000001000d4584 miniruby`ruby_xmalloc [inlined] ruby_xmalloc_body(size=<unavailable>) at gc.c:5128:12
    frame #14: 0x00000001000d4568 miniruby`ruby_xmalloc(size=<unavailable>) at gc.c:5118:34
    frame #15: 0x00000001001eb184 miniruby`rb_st_init_existing_table_with_size(tab=0x000000011c2b4b40, type=<unavailable>, size=<unavailable>) at st.c:559:39
    frame #16: 0x00000001001ebc74 miniruby`rebuild_table_if_necessary [inlined] rb_st_init_table_with_size(type=0x00000001004f4a78, size=524287) at st.c:585:5
    frame #17: 0x00000001001ebc5c miniruby`rebuild_table_if_necessary [inlined] rebuild_table(tab=0x000000013108e2f0) at st.c:753:19
    frame #18: 0x00000001001ebbfc miniruby`rebuild_table_if_necessary(tab=0x000000013108e2f0) at st.c:1125:9
    frame #19: 0x00000001001eba08 miniruby`rb_st_insert(tab=0x000000013108e2f0, key=262144, value=4767566624) at st.c:1143:5
    frame #20: 0x0000000100194b84 miniruby`ractor_port_initialzie [inlined] ractor_add_port(r=0x0000000131093460, id=262144) at ractor_sync.c:399:9
    frame #21: 0x0000000100194b58 miniruby`ractor_port_initialzie [inlined] ractor_port_init(rpv=4750065560, r=0x0000000131093460) at ractor_sync.c:87:5
    frame #22: 0x0000000100194b34 miniruby`ractor_port_initialzie(self=4750065560) at ractor_sync.c:103:12
2025-08-25 15:43:01 -04:00
Peter Zhu ad4b4f2bcc Remove dead rb_obj_is_main_ractor 2025-08-21 09:54:30 -04:00
Stan Lo 4a70f946a7 ZJIT: Implement SingleRactorMode invalidation (#14121)
* ZJIT: Implement SingleRactorMode invalidation

* ZJIT: Add macro for compiling jumps

* ZJIT: Fix typo in comment

* YJIT: Fix typo in comment

* ZJIT: Avoid using unexported types in zjit.h

`enum ruby_vminsn_type` is declared in `insns.inc` and is not exported.
Using it in `zjit.h` would cause build errors when the file including it
doesn't include `insns.inc`.
2025-08-06 13:51:41 -07:00
Jean Boussier 9b3ad3449b Mark cross_ractor_require_data_type as embeddable
Nothing prevents it, so might as well.
2025-08-06 16:37:25 +02:00
Jean Boussier 92688f7d57 variable.c: refactor accesses to the generic_fields_tbl
All accesses to `generic_fields_tbl_` are now encapsulated inside:

  - `rb_obj_fields`
  - `rb_obj_set_fields`
  - `rb_obj_replace_fields`
2025-08-06 12:33:44 +02:00
Nobuyoshi Nakada 6179cc0118 [DOC] Fill undocumented documents 2025-08-04 02:23:43 +09:00
Peter Zhu 05353ab4b7 Make cross_ractor_require write barrier protected 2025-07-31 09:14:22 -04:00
John Hawthorn 0aac763bf0 Convert cross_ractor_requires to DECL_MARKING 2025-07-30 10:18:28 -07:00
Nobuyoshi Nakada 12d44dbc49 Use an invariant condition
Cannot rule out the possibility that `crr->silent` is modified during
`func`.
2025-07-11 19:45:37 +09:00
Jean Boussier 482f4cad82 Autoload encodings on the main ractor
None of the datastructures involved in the require process are
safe to call on a secondary ractor, however when autoloading
encodings, we do so from the current ractor.

So all sorts of corruption can happen when using an autoloaded
encoding for the first time from a secondary ractor.
2025-07-07 12:44:21 +02:00
John Hawthorn 365317f6ba Fix wrong GENIV WB on too_complex Ractor traversal
WBCHECK ERROR: Missed write barrier detected!
      Parent object: 0x7c4a5f1f66c0 (wb_protected: true)
        rb_obj_info_dump: 0x00007c4a5f1f66c0 T_IMEMO/<fields>
      Reference counts - snapshot: 2, writebarrier: 0, current: 2, missed: 1
      Missing reference to: 0x7b6a5f2f7010
        rb_obj_info_dump: 0x00007b6a5f2f7010 T_ARRAY/Array [E ] len: 1 (embed)
2025-07-04 14:54:49 -07:00
John Hawthorn 5f1ca8ffbe Fix ractor imemo fields write barrier parent
$ RUBY_GC_LIBRARY=wbcheck ./miniruby -e 's = String.new; s.instance_variable_set(:@x, []); Ractor.make_shareable(s, copy: true)'

    WBCHECK ERROR: Missed write barrier detected!
      Parent object: 0x7ba8162dc890 (wb_protected: true)
        rb_obj_info_dump: 0x00007ba8162dc890 T_IMEMO/<fields>
      Reference counts - snapshot: 2, writebarrier: 0, current: 2, missed: 1
      Missing reference to: 0x7ba8162dcad0
        rb_obj_info_dump: 0x00007ba8162dcad0 T_ARRAY/Array [E ] len: 0 (embed)

    WBCHECK SUMMARY: Found 1 objects with missed write barriers (1 total violations)
2025-07-04 14:54:49 -07:00
John Hawthorn bb0d6296ac Add write barrier for hash in obj_traverse_i
We are inserting directly into the st_table, so we need to issue a write
barrier from the hash.
2025-06-18 10:08:44 -07:00
John Hawthorn f951ce37d6 Add missing writebarrier on move_leave
This object was newly allocated on move_enter, so some GC may happen and
it may have been marked by move_leave, so we need to issue an
rb_gc_writebarrier_remember so that any new references are seen afer the
memcpy from the old object.
2025-06-18 10:08:44 -07:00
Jean BoussierandÉtienne Barrié cd9f447be2 Refactor generic fields to use T_IMEMO/fields objects.
Followup: https://github.com/ruby/ruby/pull/13589

This simplify a lot of things, as we no longer need to manually
manage the memory, we can use the Read-Copy-Update pattern and
avoid numerous race conditions.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
2025-06-17 15:28:05 +02:00
Nobuyoshi Nakada 32737f8a17 Adjust indent [ci skip] 2025-06-14 15:53:25 +09:00
Jean Boussier 6dbe24fe56 Use the shape_id rather than FL_EXIVAR
We still keep setting `FL_EXIVAR` so that `rb_shape_verify_consistency`
can detect discrepancies.
2025-06-13 23:50:30 +02:00
git 4a2b53aec7 * remove trailing spaces. [ci skip] 2025-06-13 16:28:41 +00:00
Jean Boussier e22fc73c66 Fix a race condition in object_id for shareable objects
If an object is shareable and has no capacity left, it isn't
safe to store the object ID in fields as it requires an object
resize which can't be done unless all field reads are synchronized.

In this very specific case we create the object_id in advance,
before the object is made shareable.
2025-06-13 18:27:52 +02:00
John Hawthorn ef9301a6b7 Ensure crr->feature is an fstring 2025-06-12 13:13:55 -07:00
John HawthornandLuke Gruber b28f344312 Use a T_DATA for cross_ractor_require
[Bug #21090]

The struct was previously allocated on the stack, which could be freed
if the Thread is terminated. Moving this to a T_DATA on the heap should
mean this is no longer an issue.

1000.times { Ractor.new { th = Thread.new { require "rbconfig" }; Thread.pass }.take }

Co-authored-by: Luke Gruber <luke.gruber@shopify.com>
2025-06-12 13:13:55 -07:00
Jean Boussier f9966b9b76 Get rid of gen_fields_tbl.fields_count
This data is redundant because the shape already contains both the
length and capacity of the object's fields.

So it both waste space and create the possibility of a desync between
the two.

We also do not need to initialize everything to Qundef, this seem
to be a left-over from pre-shape instance variables.
2025-06-09 16:38:29 +02:00
Koichi Sasada 1605704117 ignore confirming belonging while finrializer
A finalizer registerred in Ractor A can be invoked in B.

```ruby
require "tempfile"
r = Ractor.new{
  10_000.times{|i|
    Tempfile.new(["file_to_require_from_ractor#{i}", ".rb"])
  }
}
sleep 0.1
```

For example, above script makes tempfiles which have finalizers
on Ractor r, but at the end of the process, main Ractor will invoke
finalizers and it violates belonging check. This patch just ignore
the belonging check to avoid CI failure.

Of course it violates Ractor's isolation and wrong workaround.
This issue will be solved with Ractor local GC.
2025-06-07 09:52:03 +09:00
Koichi Sasada 0ca80484ac mark main Ractor object
`RUBY_DEBUG=gc_stress ./miniruby -e0` crashes because of this
marking miss.

BTW, to use `RUBY_DEBUG=gc_stress` we need to specify
`--enable-debug-env` configure option. This is why I couldn't repro
on my environments.

see c0c94ab183
2025-06-05 08:15:57 +09:00
Daisuke Aritomo 2097d3794d Fix typo (s/ractore/ractor/) 2025-05-31 18:31:10 +09:00