| Age | Commit message (Collapse) | Author |
|
Previously, local indices greater than 255 were truncated when
converted to RegOpnd::Local. This could make a high-index local alias
a tracked low-index local in the register mapping and produce incorrect
values after compilation.
Cap overflowing indices to MAX_CTX_LOCALS. RegMapping treats that index
as untrackable because it is just outside the tracked local range.
Fixes [Bug #22074].
Co-authored-by: Codex <199175422+chatgpt-connector[bot]@users.noreply.github.com>
|
|
Fixes [Bug #22075]
|
|
The `too_` prefix wasn't consistently used and just make the
thing longer for no benefit.
|
|
As well as `putchilledstring` as `dupchilledstring`.
This is more consistent with similar `duparray` and `duphash`
instructions and better reflect it's behavior.
|
|
[Bug #21818]
Currently exceptions can be sent across ractors,
but because of a limitation in the TypedData API,
the exception backtrace is duped as an empty backtrace.
The problem is that backtraces are embedded objects,
hence the classic `rb_class_alloc(klass)` API is insufficient
because we need to know the size of the Backtrace object we're
duping to instantiate the copy.
This is worked around by changing Ractors to call `#clone` on objects
rather than use `rb_obj_clone`, and to implement `Thread::Backtrace#clone`
to properly clone the variable size object.
|
|
This reverts commit 1596853428393136ee9964ad4c11b0120ed648d1.
Expressions that run when the local is not given can observe the initial
state of the optional parameter locals.
|
|
Today you can read instance variables from non-main Ractors, but many
Rails applications use cvars, and we cannot read them.
For example:
```ruby
class Foo
# This is NOT allowed to be read in non-main Ractors
@@bar = 123
def self.bar; @@bar; end
# This is allowed to be read in non-main Ractors
@baz = 123
def self.baz; @baz; end
end
# This is OK
Ractor.new {
p Foo.baz
}.value
# Exception here
Ractor.new {
p Foo.bar
}.value
```
This commit changes the semantics of cvars to be the same as instance
variables:
* It's ok to read Ractor shareable objects from the non-main Ractor
* It's NOT ok to write from the non-main Ractor
[Feature #21942]
|
|
Previously, when passing an unshareable proc to a Ractor we would get
the message:
'Ractor.new': allocator undefined for Proc (TypeError)
With this change we get:
'Ractor.new': can not copy unshareable object #<Proc:0x00007f1b31713600 test.rb:2> (Ractor::Error)
Which should hopefully be a little clearer about the problem and make it
simpler to debug.
|
|
Multiple YJIT functions created overlapping `&'static mut IseqPayload`
references by calling `get_iseq_payload()` multiple times for the same
iseq. Overlapping &mut is UB in rust's aliasing model, and as consequence,
we trigered use-after-free on the `version_map` Vec header due to false
claims of LLVM `noalias`.
This manifested as crashes in various YJIT operations (block lookup,
GC marking, block removal) that dereference the stale pointer.
Fix by moving `delayed_deallocation` and `get_or_create_version_list`
from free functions (which each call `get_iseq_payload()` internally)
to methods on `IseqPayload` that operate through `&mut self`. This
lets callers obtain a single payload reference and use it for all
operations without creating overlapping mutable borrows.
The three fixed call sites:
1. `rb_yjit_tracing_invalidate_all` (invariants.rs): The loop called
`delayed_deallocation()` which internally called `get_iseq_payload()`,
creating a second `&mut` overlapping with the outer `payload` reference.
Fix: call `payload.delayed_deallocation()` method instead.
2. `add_block_version` (core.rs): Called `get_or_create_version_list()`
then later `get_iseq_payload()` for pages, creating two references.
Fix: use a single `get_or_create_iseq_payload()` call then call the
`get_or_create_version_list()` method on it for both version_map and
pages access.
Also adds regression tests exercising tracing invalidation with
on-stack methods and suspended fibers.
[alan: edited commit message]
Reviewed-by: Alan Wu <alanwu@ruby-lang.org>
|
|
Previously, when dealing with a `super()` nested in a block that runs as
a method (through e.g. `define_method`), YJIT generated a guard that
never passes leading to a misidentification of the callsite as
megamorphic and an unconditional interpreter fallback.
The issue was in the subroutine to find the currently running method
entry. In the interpreter, this is rb_vm_frame_method_entry(). YJIT used
`gen_get_lep()` to find the EP with `VM_ENV_FLAG_LOCAL`, but in case
of BMETHODs, the corresponding CME is never at an EP level with
`VM_ENV_FLAG_LOCAL` set.
Because each block nesting level can dynamically run as either a BMETHOD
or not, starting at a block and finding the first EP that has a method
entry ultimately requires a search loop such as the one in rb_vm_frame_method_entry().
This patch introduces such a loop.
Because `invokesuper` in a block can now work end-to-end, add check for the
previously masked "implicit argument passing of super from method
defined by define_method() is not supported..." condition.
|
|
argument error
|
|
|
|
Example insns diff for `def x = [3]; def a(...) = b(*x, 2, 3, ...)`
== disasm: #<ISeq:a@-e:1 (1,13)-(1,42)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself ( 1)[Ca]
0000 putself
0000 opt_send_without_block <calldata!mid:x, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0000 splatarray true
0000 putobject 2
0000 putobject 3
+0000 pushtoarray 2
0000 getlocal_WC_0 "..."@0
0000 sendforward <calldata!mid:b, argc:1, ARGS_SPLAT|ARGS_SPLAT_MUT|FCALL|FORWARDING>, nil
0000 leave [Re]
This matches the insns produced by parse.y
|
|
A placeholder to handle GNU make jobserver option.
spec/default.mspec didn't handle the jobserver using a FIFO.
|
|
|
|
Before this patch, Ractor::IsolationError reported an incorrect constant
path when constant was found through `rb_const_get_0()`.
In this code, Ractor::IsolationError reported illegal access against
`M::TOPLEVEL`, where it should be `Object::TOPLEVEL`.
```ruby
TOPLEVEL = [1]
module M
def self.f
TOPLEVEL
end
end
Ractor.new { M.f }.value
```
This was because `rb_const_get_0()` built the "path" part referring to
the module/class passed to it in the first place. When a constant was
found through recursive search upwards, the module/class which the
constant was found should be reported.
This patch fixes this issue by modifying rb_const_search() to take a
VALUE pointer to be filled with the module/class where the constant was
found.
[Bug #21782]
|
|
|
|
|
|
Fixes [Bug #21266].
|
|
Credits to @rwstauner for noticing this issue in GH-15533.
|
|
Previously, the chain_depth>0 version of setlocal blocks did not
update the type of the local variable in the context. This can leave
the context with stale type information and trigger panics like in
[Bug #21772] or lead to miscompilation.
To trigger the issue, YJIT needs to see the same ISEQ before and after
environment escape and have tracked type info before the escape. To
trigger in ISEQs that do not send with a block, it probably requires
Kernel#binding or the use of include/ruby/debug.h APIs.
|
|
|
|
Although the Ractor API is still experimental and may change, and there
may be some implementation issues, we should no longer say that there
are many.
Hopefully we can remove this warning entirely for Ruby 4.1
|
|
This refactors the concurrent set to examine and reserve a slot via CAS
with the hash, before then doing the same with the key.
This allows us to use an extra bit from the hash as a "continuation bit"
which marks whether we have ever probed past this key while inserting.
When that bit isn't set on deletion we can clear the field instead of
placing a tombstone.
|
|
|
|
|
|
|
|
|
|
on resource limited environment.
```
stderr output is not empty
bootstraptest.test_ractor.rb_2446_1412.rb:23:in 'Ractor.new': can't create Thread: Cannot allocate memory (ThreadError)
```
|
|
The id2ref table needs to be under a VM lock to ensure there are no race
conditions. The following script crashes:
o = Object.new
ObjectSpace._id2ref(o.object_id)
10.times.map do
Ractor.new do
10_000.times do
a = Object.new
a.object_id
end
end
end.map(&:value)
With:
[BUG] Object ID seen, but not in _id2ref table: object_id=2800 object=T_OBJECT
ruby 4.0.0dev (2025-12-06T15:15:43Z ractor-id2ref-fix e7f9abdc91) +PRISM [x86_64-linux]
-- Control frame information -----------------------------------------------
c:0001 p:---- s:0003 e:000002 l:y b:---- DUMMY [FINISH]
-- Threading information ---------------------------------------------------
Total ractor count: 5
Ruby thread count for this ractor: 1
-- C level backtrace information -------------------------------------------
miniruby(rb_print_backtrace+0x14) [0x6047d09b2dff] vm_dump.c:1105
miniruby(rb_vm_bugreport) vm_dump.c:1450
miniruby(rb_bug_without_die_internal+0x5f) [0x6047d066bf57] error.c:1098
miniruby(rb_bug) error.c:1116
miniruby(rb_gc_get_ractor_newobj_cache+0x0) [0x6047d066c8dd] gc.c:2052
miniruby(gc_sweep_plane+0xad) [0x6047d079276d] gc/default/default.c:3513
miniruby(gc_sweep_page) gc/default/default.c:3605
miniruby(gc_sweep_step) gc/default/default.c:3886
miniruby(gc_sweep+0x1ba) [0x6047d0794cfa] gc/default/default.c:4154
miniruby(gc_start+0xbf2) [0x6047d0796742] gc/default/default.c:6519
miniruby(heap_prepare+0xcc) [0x6047d079748c] gc/default/default.c:2090
miniruby(heap_next_free_page) gc/default/default.c:2305
miniruby(newobj_cache_miss) gc/default/default.c:2412
miniruby(newobj_alloc+0xd) [0x6047d0798ff5] gc/default/default.c:2436
miniruby(rb_gc_impl_new_obj) gc/default/default.c:2515
miniruby(newobj_of) gc.c:996
miniruby(rb_wb_protected_newobj_of) gc.c:1046
miniruby(str_alloc_embed+0x28) [0x6047d08fda18] string.c:1019
miniruby(str_enc_new) string.c:1069
miniruby(prep_io+0x5) [0x6047d07cda14] io.c:9305
miniruby(prep_stdio) io.c:9347
miniruby(rb_io_prep_stdin) io.c:9365
miniruby(thread_start_func_2+0x77c) [0x6047d093a55c] thread.c:679
miniruby(thread_sched_lock_+0x0) [0x6047d093aacd] thread_pthread.c:2241
miniruby(co_start) thread_pthread_mn.c:469
|
|
`pr` should not change on this method.
|
|
|
|
When defining a bmethod, we recorded the current Ractor's object in the
method. However that was never marked and so could be GC'd and reused by
a future Ractor. Instead we can use the Ractor's id, which we expect to
be unique forever.
Co-authored-by: Luke Gruber <luke.gru@gmail.com>
|
|
|
|
with `RUBY_TYPED_FROZEN_SHAREABLE_NO_REC`,
if the receiver object is shareable on Method objects.
|
|
Fixes: [Bug #21707]
[AW: rewrote comments]
Co-authored-by: Alan Wu <alanwu@ruby-lang.org>
|
|
Since we do not run a Ractor barrier before forking, it's possible that
another other Ractor is halfway through allocating an object during forking.
This may lead to allocated_objects_count being off by one.
For example, the following script reproduces the bug:
100.times do |i|
Ractor.new(i) do |j|
10000.times do |i|
"#{j}-#{i}"
end
Ractor.receive
end
pid = fork { GC.verify_internal_consistency }
_, status = Process.waitpid2 pid
raise unless status.success?
end
We need to run with `taskset -c 1` to force it to use a single CPU core
to more consistenly reproduce the bug:
heap_pages_final_slots: 1, total_freed_objects: 16628
test.rb:8: [BUG] inconsistent live slot number: expect 19589, but 19588.
ruby 4.0.0dev (2025-11-25T03:06:55Z master 55892f5994) +PRISM [x86_64-linux]
-- Control frame information -----------------------------------------------
c:0007 p:---- s:0029 e:000028 l:y b:---- CFUNC :verify_internal_consistency
c:0006 p:0004 s:0025 e:000024 l:n b:---- BLOCK test.rb:8 [FINISH]
c:0005 p:---- s:0022 e:000021 l:y b:---- CFUNC :fork
c:0004 p:0012 s:0018 E:0014c0 l:n b:---- BLOCK test.rb:8
c:0003 p:0024 s:0011 e:000010 l:y b:0001 METHOD <internal:numeric>:257
c:0002 p:0005 s:0006 E:001730 l:n b:---- EVAL test.rb:1 [FINISH]
c:0001 p:0000 s:0003 E:001d20 l:y b:---- DUMMY [FINISH]
-- Ruby level backtrace information ----------------------------------------
test.rb:1:in '<main>'
<internal:numeric>:257:in 'times'
test.rb:8:in 'block in <main>'
test.rb:8:in 'fork'
test.rb:8:in 'block (2 levels) in <main>'
test.rb:8:in 'verify_internal_consistency'
-- Threading information ---------------------------------------------------
Total ractor count: 1
Ruby thread count for this ractor: 1
-- C level backtrace information -------------------------------------------
ruby(rb_print_backtrace+0x14) [0x61b67ac48b60] vm_dump.c:1105
ruby(rb_vm_bugreport) vm_dump.c:1450
ruby(rb_bug_without_die_internal+0x5f) [0x61b67a818a28] error.c:1098
ruby(rb_bug) error.c:1116
ruby(gc_verify_internal_consistency_+0xbdd) [0x61b67a83d8ed] gc/default/default.c:5186
ruby(gc_verify_internal_consistency+0x2d) [0x61b67a83d960] gc/default/default.c:5241
ruby(rb_gc_verify_internal_consistency) gc/default/default.c:8950
ruby(gc_verify_internal_consistency_m) gc/default/default.c:8966
ruby(vm_call_cfunc_with_frame_+0x10d) [0x61b67a9e50fd] vm_insnhelper.c:3902
ruby(vm_sendish+0x111) [0x61b67a9eeaf1] vm_insnhelper.c:6124
ruby(vm_exec_core+0x84) [0x61b67aa07434] insns.def:903
ruby(vm_exec_loop+0xa) [0x61b67a9f8155] vm.c:2811
ruby(rb_vm_exec) vm.c:2787
ruby(vm_yield_with_cref+0x90) [0x61b67a9fd2ea] vm.c:1865
ruby(vm_yield) vm.c:1873
ruby(rb_yield) vm_eval.c:1362
ruby(rb_protect+0xef) [0x61b67a81fe6f] eval.c:1154
ruby(rb_f_fork+0x16) [0x61b67a8e98ab] process.c:4293
ruby(rb_f_fork) process.c:4284
|
|
This was a test case for Ractors discovered that causes MMTk to deadlock.
There is a fix for it in https://github.com/ruby/mmtk/pull/49.
|
|
The comptime receiver, which is a proc, is either shareable or from this
ractor so we don't need to assume single-ractor mode. We should never get
the "defined with an un-shareable Proc in a different ractor" error.
|
|
Previously because we did a stack_push before ccall, in some cases we
could end up pushing an uninitialized value to the VM stack when
spilling regs as part of the ccall.
Co-authored-by: Luke Gruber <luke.gru@gmail.com>
|
|
This was a mistake;
the code tested for RUBY_PATCHLEVEL but then
instead of using it used RUBY_PLATFORM twice.
|
|
https://github.com/ruby/ruby/actions/runs/19107764906/job/54596244201
|
|
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]
|
|
|
|
|
|
|
|
|
|
because ISeq is shareable now.
|
|
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]
|
|
ZJIT: Unskip Ractor bootstrap test
|