| Age | Commit message (Collapse) | Author |
|
|
|
If the slot_size < RVALUE_SIZE then we would underflow in the memset.
|
|
Change example to use user-defined class instead of `Numeric`.
|
|
This struct is used for addition not only for multiplication, so
remove the word `mul`, and make the member names more descriptive.
|
|
Help the compiler know that we always get a heap object here.
|
|
These macros have been defined here and there, so collect them.
|
|
|
|
|
|
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
|
|
This reverts commit 228d13f6ed914d1e7f6bd2416e3f5be8283be865.
This commit makes default.c and mmtk.c depend on shape.h, which prevents
them from building independently.
|
|
[Feature #20750]
Co-authored-by: Benoit Daloze <eregontp@gmail.com>
|
|
If GC trigger in the middle of `struct_alloc`, and the struct has more
than 3 elements, then `fields_obj` reference is garbage.
We must first check the shape to know if it was actually initialized.
|
|
Attempt to fix the following SEGV:
```
ruby(gc_mark) ../src/gc/default/default.c:4429
ruby(gc_mark_children+0x45) [0x560b380bf8b5] ../src/gc/default/default.c:4625
ruby(gc_mark_stacked_objects) ../src/gc/default/default.c:4647
ruby(gc_mark_stacked_objects_all) ../src/gc/default/default.c:4685
ruby(gc_marks_rest) ../src/gc/default/default.c:5707
ruby(gc_marks+0x4e7) [0x560b380c41c1] ../src/gc/default/default.c:5821
ruby(gc_start) ../src/gc/default/default.c:6502
ruby(heap_prepare+0xa4) [0x560b380c4efc] ../src/gc/default/default.c:2074
ruby(heap_next_free_page) ../src/gc/default/default.c:2289
ruby(newobj_cache_miss) ../src/gc/default/default.c:2396
ruby(RB_SPECIAL_CONST_P+0x0) [0x560b380c5df4] ../src/gc/default/default.c:2420
ruby(RB_BUILTIN_TYPE) ../src/include/ruby/internal/value_type.h:184
ruby(newobj_init) ../src/gc/default/default.c:2136
ruby(rb_gc_impl_new_obj) ../src/gc/default/default.c:2500
ruby(newobj_of) ../src/gc.c:996
ruby(rb_imemo_new+0x37) [0x560b380d8bed] ../src/imemo.c:46
ruby(imemo_fields_new) ../src/imemo.c:105
ruby(rb_imemo_fields_new) ../src/imemo.c:120
```
I have no reproduction, but my understanding based on the backtrace
and error is that GC is triggered inside `newobj_init` causing the
new object to be marked while in a incomplete state.
I believe the fix is to pass the `shape_id` down to `newobj_init`
so it can be set before the GC has a chance to trigger.
|
|
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`.
|
|
[Bug #21710]
- struct.c: `struct_alloc`
It is possible for a `NEWOBJ` tracepoint call back to write fields
into a newly allocated object before `struct_alloc` had the time
to set the `RSTRUCT_GEN_FIELDS` flags and such.
Hence we can't blindly initialize the `fields_obj` reference to `0`
we first need to check no fields were added yet.
- object.c: `rb_class_allocate_instance`
Similarly, if a `NEWOBJ` tracepoint tries to set fields on the object,
the `shape_id` must already be set, as it's required on T_OBJECT to
know where to write fields.
`NEWOBJ_OF` had to be refactored to accept a `shape_id`.
|
|
When a class/module defined by extension libraries in a box, checking
types of instances of the class needs to access its data type (rb_data_type_t).
So if a class still exists (not GCed), the box must exist too (to be marked).
|
|
|
|
|
|
Previously any T_OBJECT with >= 94 IVARs would crash during compaction
attempting to make an object too large to embed.
|
|
It may return sizes that aren't allocatable for arrays and strings.
|
|
rb_gc_verify_shareable is not GC implementation specific so it should live
in gc.c.
|
|
|
|
We shouldn't run any ruby code with the VM lock held.
|
|
This implements it the same as the other modular GC functions
|
|
|
|
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
|
|
|
|
* ZJIT: Add NoSingletonClass patch point
This patch point makes sure that when the object has a singleton class,
the JIT code is invalidated. As of now, this is only needed for C call
optimization.
In YJIT, the singleton class guard only applies to Array, Hash, and String.
But in ZJIT, we may optimize C calls from gems (e.g. `sqlite3`). So the
patch point needs to be applied to a broader range of classes.
* ZJIT: Only generate NoSingletonClass guard when the type can have singleton class
* ZJIT: Update or forget NoSingletonClass patch point when needed
|
|
Add side exit tracing functionality for ZJIT
|
|
Previously unused.
|
|
ZJIT doesn't set PC before rb_set_ivar(), and that allocates a managed
ID table. Was a false positive from gc_validate_pc().
|
|
The validation is relevant only for traceable userland ruby objects ruby
code could interact with. ZJIT's use of rb_vm_method_cfunc_is()
allocates a CC imemo and was failing this validation when it was
actually fine. Relax the check.
|
|
If another ractor is calling for GC, we need to prevent the current one
from joining the barrier. Otherwise, our half-built object will be marked.
The repro script was:
test.rb:
```ruby
require "objspace"
1000.times do
ObjectSpace.trace_object_allocations do
r = Ractor.new do
_obj = 'a' * 1024
end
r.join
end
end
```
$ untilfail lldb -b ./exe/ruby -o "target create ./exe/ruby" -o "run test.rb" -o continue
It would fail at `ractor_port_mark`, rp->r was a garbage value. Credit to John for finding the
solution.
Co-authored-by: John Hawthorn <john.hawthorn@shopify.com>
|
|
|
|
Setting v1, v2, v3 when we allocate an object assumes that we always
allocate 40 byte objects. By removing v1, v2, v3, we can make the base
slot size another size.
|
|
Make this macro condition as compile-time constant instead of a
preprocess-time constant, and compile the body always.
|
|
This commit fills the space that is not initialized with garbage for
newly allocated objects on debug mode.
|
|
Ref: https://github.com/ruby/ruby/pull/14134#issuecomment-3207733725
We can't safely use low-bit pointer tagging anymore because `RTypedData.type`
lines up with `RData.dfree` and there is no aligment guarantee on function
pointers, as evidenced by `memcached` and `gpgme` gems.
We also can't use FL_USER* for this, because extensions may use these
for other purposes.
Using a general flag for this is a bit unfortunate, as general flags
are hard to come by, however I recently freed several of them, and
we still have two or three free ones left.
|
|
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.
|
|
* To not count them as program allocations.
* Similar to https://github.com/ruby/ruby/pull/13906
|
|
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
|
|
Now that the shape_id has been unified across all types
this helper function doesn't do much over `RBASIC_SET_SHAPE_ID`.
It still check if the write is needed, but it doesn't seem useful
in places where it's used.
|
|
|
|
|
|
|
|
Since we handle embedded arrays in the if statement above, we don't need
to handle it here.
|
|
rb_gc_shutdown_call_finalizer_p returns false for threads and fibers, so
it should probably do the same for all Ractors (not just the main one).
This hopefully mitigates a bug where, at exit, rb_ractor_terminate_all
gets all Ractors to stop before continuing with the shutdown process.
However when vm->ractor.cnt reaches 1, the native threads may still be
running code at the end co_start, which reads/locks on
th->ractor->threads.sched, so the Ractor is not safe to free.
A better solution might be to ensure that all native threads end up
stopped or otherwise parked before this part of the shutdown, however
that would be a bit more involved.
|
|
It is much more convenient than storing the klass, especially
when dealing with `object_id` as it allows to update the id2ref
table without having to dereference the owner, which may be
garbage at that point.
|
|
Similar to f3206cc79bec2fd852e81ec56de59f0a67ab32b7 but for TypedData.
It's quite common for TypedData objects to have a mix of reference in
their struct and some ivars.
Since we do happen to have 8B free in the RtypedData struct, we could
use it to keep a direct reference to the IMEMO/fields saving having
to synchronize the VM and lookup the `gen_fields_tbl` on every ivar
access.
For old school Data classes however, we don't have free space, but
this API is soft-deprecated and no longer very common.
|
|
gc_config_set returned rb_gc_impl_config_get, but gc_config_get also added
the implementation key to the return value. This caused the return value
of GC.config to differ depending on whether the optional hash argument is
provided or not.
|