Commit graph

201 commits

Author SHA1 Message Date
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 Boussier
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 Hawthorn
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
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
Peter Zhu
386f874816 Don't copy FL_PROMOTED to new object in Ractor move
We should not copy the FL_PROMOTED flag when we move an object in Ractor#send(move: true)
because the newly created object may not be old.
2025-05-26 15:04:00 -04: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
Nobuyoshi Nakada
aad9fa2853
Use RB_VM_LOCKING 2025-05-25 15:22:43 +09:00
Luke Gruber
2b5a674440 ractor_wakeup was broken when compiled with USE_RUBY_DEBUG_LOG
The `ractor_wakeup` function takes an optional `th` argument, so it can be NULL.
There is a macro call to RUBY_DEBUG_LOG that dereferences `th` without checking
if it's NULL first. To fix this, we never dereference `th` in this macro call.
2025-05-23 16:02:48 -07:00
Luke Gruber
f64c89f18d Fix 'require' from a ractor when the required file raises an error
If you catch an error that was raised from a file you required in
a ractor, that error did not have its belonging reset from the main
ractor to the current ractor, so you hit assertion errors in debug
mode.
2025-05-23 21:13:23 +02:00
Peter Zhu
746d7fef92 Fix moving old objects between Ractors
The FL_PROMOTED flag was not copied when moving objects, causing assertions
to fail when an old object is moved:

    gc/default/default.c:834: Assertion Failed: RVALUE_AGE_SET:age <= RVALUE_OLD_AGE

Co-Authored-By: Luke Gruber <luke.gruber@shopify.com>
2025-05-23 11:06:53 -04:00
John Hawthorn
7b10660974 Use rb_inspect for Ractor error
Previously the object was used directly, which calls `to_s` if defined.
We should use rb_inspect to get a value suitable for display to the
programmer.
2025-05-21 12:23:14 -07:00
Nobuyoshi Nakada
2e3f81838c
Align styles [ci skip] 2025-05-15 17:48:40 +09:00
Luke Gruber
1d4822a175 Get ractor message passing working with > 1 thread sending/receiving values in same ractor
Rework ractors so that any ractor action (Ractor.receive, Ractor#send, Ractor.yield, Ractor#take,
Ractor.select) will operate on the thread that called the action. It will put that thread to sleep if
it's a blocking function and it needs to put it to sleep, and the awakening action (Ractor.yield,
Ractor#send) will wake up the blocked thread.

Before this change every blocking ractor action was associated with the ractor struct and its fields.
If a ractor called Ractor.receive, its wait status was wait_receiving, and when another ractor calls
r.send on it, it will look for that status in the ractor struct fields and wake it up. The problem was that
what if 2 threads call blocking ractor actions in the same ractor. Imagine if 1 thread has called Ractor.receive
and another r.take. Then, when a different ractor calls r.send on it, it doesn't know which ruby thread is associated
to which ractor action, so what ruby thread should it schedule? This change moves some fields onto the ruby thread
itself so that ruby threads are the ones that have ractor blocking statuses, and threads are then specifically scheduled
when unblocked.

Fixes [#17624]
Fixes [#21037]
2025-05-13 13:23:57 -07:00
Jean Boussier
5974841d11 Remove outdated references to FL_SEEN_OBJ_ID 2025-05-13 12:02:19 +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
Aaron Patterson
e3452cfad2 Raise error on take/send for Ractors in child processes
Ractor objects that are available in a child process should raise a
`Ractor::ClosedError` exception when called with `send` or `take`

Co-authored-by: John Hawthorn <john@hawthorn.email>
2025-05-08 10:53:28 -07:00
Aaron Patterson
f7ff380998 Clean up Ractor cache after fork
Ractors created in a parent process should be properly shut down in the
child process.  They need their cache cleared and status set to
"terminated"

Co-authored-by: John Hawthorn <john@hawthorn.email>
2025-05-08 10:53:28 -07: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
John Hawthorn
7866e124a8 Use rb_current_ec_noinline in assertions
When doing a coroutine transfer from one thread to another, there's a
risk that the compiler will reuse an address from TLS before the
transfer to the new thread.

These VM assertions are all in places we would not otherwise be reading
from TLS, but using the value of `ec` or `cr` passed in. Switching these
to test against rb_current_ec_noinline() instead ensures there isn't an
optimization applied to how we read ruby_current_ec.

Currently it seems we were hitting this on LLVM 18 specifically, but I
don't know of any reason other versions wouldn't have the same issue.
2025-04-30 15:36:58 -07: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
John Hawthorn
137b51e4d3 Remove rb_gc_start from cancel_single_ractor_mode
In 307732ccee Ractors were changed to
explicitly run GC when the first non-main one was activated in order to
disable the transient heap. Theap no longer exists so I don't think we
need to do this.
2025-04-03 10:39:47 -07: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
lukeg
d80f3a287c Ractor.make_shareable(proc_obj) makes inner structure shareable
Proc objects are now traversed like other objects when making them
shareable.

Fixes [Bug #19372]
Fixes [Bug #19374]
2025-03-26 16:05:02 -07:00
Nobuyoshi Nakada
4a67ef09cc
[Feature #21116] Extract RJIT as a third-party gem 2025-02-13 18:01:03 +09:00
Nobuyoshi Nakada
92f850ae84
[DOC] Hide Ractor::Selector
It is not enabled by default currently.
2024-12-25 11:13:07 +09:00
lukeg
0d81177c20 Fix calls to require_internal in multi-ractor mode
After a ractor is started (multi-ractor mode), any calls to
require_internal will hang the process due to deadlock. For example,
loading a new encoding will deadlock after a ractor starts.

Fixes [Bug #19562]
2024-12-24 11:40:00 +09:00
Luke Gruber
38af38edcb Fix ractor move of unshareable frozen objects
These objects didn't retain their frozen status after the move

Bug [#19408]
2024-12-24 11:38:44 +09:00
Yudai Takada
b0d40d3d03
[DOC] Fix typos in comments in ractor.c 2024-12-22 18:08:39 +09:00
Koichi Sasada
e09c23433e followup 0bdb38ba6b
(forgot to amend...)
2024-12-13 17:05:58 +09:00