From 04e90fe200d736db0a32a794b8dc742fa0cb5441 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Sat, 20 Dec 2025 03:49:50 +0900 Subject: [PATCH 1/6] skip TestFiberScheduler#test_io_write_on_flush because it makes GC.stat test fragile --- test/fiber/test_scheduler.rb | 49 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 4a8b4ee62d8e49..c20fe86ff4531d 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -287,33 +287,36 @@ def test_post_fork_fiber_blocking end def test_io_write_on_flush - fn = File.join(Dir.tmpdir, "ruby_test_io_write_on_flush_#{SecureRandom.hex}") - write_fd = nil - io_ops = nil - thread = Thread.new do - scheduler = IOScheduler.new - Fiber.set_scheduler scheduler + omit "skip this test because it makes CI fragile" + begin + fn = File.join(Dir.tmpdir, "ruby_test_io_write_on_flush_#{SecureRandom.hex}") + write_fd = nil + io_ops = nil + thread = Thread.new do + scheduler = IOScheduler.new + Fiber.set_scheduler scheduler - Fiber.schedule do - File.open(fn, 'w+') do |f| - write_fd = f.fileno - f << 'foo' - f.flush - f << 'bar' + Fiber.schedule do + File.open(fn, 'w+') do |f| + write_fd = f.fileno + f << 'foo' + f.flush + f << 'bar' + end end + io_ops = scheduler.__io_ops__ end - io_ops = scheduler.__io_ops__ - end - thread.join - assert_equal [ - [:io_write, write_fd, 'foo'], - [:io_write, write_fd, 'bar'] - ], io_ops + thread.join + assert_equal [ + [:io_write, write_fd, 'foo'], + [:io_write, write_fd, 'bar'] + ], io_ops - assert_equal 'foobar', IO.read(fn) - ensure - thread.kill rescue nil - FileUtils.rm_f(fn) + assert_equal 'foobar', IO.read(fn) + ensure + thread.kill rescue nil + FileUtils.rm_f(fn) + end end def test_io_read_error From d9c0d4c71cd3500b2307c518b423ee58eeff9ae5 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Fri, 19 Dec 2025 02:21:55 -0800 Subject: [PATCH 2/6] Don't copy invalidated CME in rb_vm_cc_table_dup The cc_entries list associated with the invalidated CME can be deleted from the table during GC, so it isn't safe to copy (and we shouldn't copy it anyways, it's stale data). --- vm_method.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vm_method.c b/vm_method.c index 2a6323e59300b5..2b3ac74d573434 100644 --- a/vm_method.c +++ b/vm_method.c @@ -149,10 +149,21 @@ vm_cc_table_dup_i(ID key, VALUE old_ccs_ptr, void *data) { VALUE new_table = (VALUE)data; struct rb_class_cc_entries *old_ccs = (struct rb_class_cc_entries *)old_ccs_ptr; + + if (METHOD_ENTRY_INVALIDATED(old_ccs->cme)) { + // Invalidated CME. This entry will be removed from the old table on + // the next GC mark, so it's unsafe (and undesirable) to copy + return ID_TABLE_CONTINUE; + } + size_t memsize = vm_ccs_alloc_size(old_ccs->capa); struct rb_class_cc_entries *new_ccs = ruby_xcalloc(1, memsize); rb_managed_id_table_insert(new_table, key, (VALUE)new_ccs); + // We hold the VM lock, so invalidation should not have happened between + // our earlier invalidation check and now. + VM_ASSERT(!METHOD_ENTRY_INVALIDATED(old_ccs->cme)); + memcpy(new_ccs, old_ccs, memsize); #if VM_CHECK_MODE > 0 @@ -169,6 +180,7 @@ vm_cc_table_dup_i(ID key, VALUE old_ccs_ptr, void *data) VALUE rb_vm_cc_table_dup(VALUE old_table) { + ASSERT_vm_locking(); VALUE new_table = rb_vm_cc_table_create(rb_managed_id_table_size(old_table)); rb_managed_id_table_foreach(old_table, vm_cc_table_dup_i, (void *)new_table); return new_table; From d540903ee77e81dd1ec3dec744273fbb394238fa Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 19 Dec 2025 12:45:36 +0000 Subject: [PATCH 3/6] [DOC] Harmonize <= methods --- compar.c | 11 ++++++++--- hash.c | 5 ++--- numeric.c | 9 +++++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compar.c b/compar.c index c577e4741ffa13..eda7e83824a4e0 100644 --- a/compar.c +++ b/compar.c @@ -141,10 +141,15 @@ cmp_lt(VALUE x, VALUE y) /* * call-seq: - * obj <= other -> true or false + * self <= other -> true or false + * + * Returns whether +self+ is "less than or equal to" +other+; + * equivalent to (self <=> other) <= 0: + * + * 'foo' <= 'foo' # => true + * 'foo' <= 'food' # => true + * 'food' <= 'foo' # => false * - * Compares two objects based on the receiver's <=> - * method, returning true if it returns a value less than or equal to 0. */ static VALUE diff --git a/hash.c b/hash.c index 9e1555518ec037..3669f55d5024d0 100644 --- a/hash.c +++ b/hash.c @@ -4888,10 +4888,9 @@ hash_le(VALUE hash1, VALUE hash2) /* * call-seq: - * self <= other_hash -> true or false + * self <= other -> true or false * - * Returns +true+ if the entries of +self+ are a subset of the entries of +other_hash+, - * +false+ otherwise: + * Returns whether the entries of +self+ are a subset of the entries of +other+: * * h0 = {foo: 0, bar: 1} * h1 = {foo: 0, bar: 1, baz: 2} diff --git a/numeric.c b/numeric.c index 3e770ceed3bc88..a71060c55db252 100644 --- a/numeric.c +++ b/numeric.c @@ -1738,7 +1738,8 @@ flo_lt(VALUE x, VALUE y) * call-seq: * self <= other -> true or false * - * Returns +true+ if +self+ is numerically less than or equal to +other+: + * Returns whether the value of +self+ is less than or equal to the value of +other+; + * +other+ must be numeric, but may not be Complex: * * 2.0 <= 3 # => true * 2.0 <= 3.0 # => true @@ -5081,10 +5082,10 @@ fix_le(VALUE x, VALUE y) /* * call-seq: - * self <= real -> true or false + * self <= other -> true or false * - * Returns +true+ if the value of +self+ is less than or equal to - * that of +other+: + * Returns whether the value of +self+ is less than or equal to the value of +other+; + * +other+ must be numeric, but may not be Complex: * * 1 <= 0 # => false * 1 <= 1 # => true From 42c4df9ac6d5974eac7efddf725ecf39f4c0fd9e Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 19 Dec 2025 20:06:49 +0000 Subject: [PATCH 4/6] [DOC] Harmonize String#<=> --- string.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/string.c b/string.c index 9819a5910fa76e..b11b441ac58536 100644 --- a/string.c +++ b/string.c @@ -4294,23 +4294,26 @@ rb_str_eql(VALUE str1, VALUE str2) /* * call-seq: - * self <=> other_string -> -1, 0, 1, or nil + * self <=> other -> -1, 0, 1, or nil * - * Compares +self+ and +other_string+, returning: + * Compares +self+ and +other+, + * evaluating their _contents_, not their _lengths_. * - * - -1 if +other_string+ is larger. - * - 0 if the two are equal. - * - 1 if +other_string+ is smaller. - * - +nil+ if the two are incomparable. + * Returns: + * + * - +-1+, if +self+ is smaller. + * - +0+, if the two are equal. + * - +1+, if +self+ is larger. + * - +nil+, if the two are incomparable. * * Examples: * - * 'foo' <=> 'foo' # => 0 - * 'foo' <=> 'food' # => -1 - * 'food' <=> 'foo' # => 1 - * 'FOO' <=> 'foo' # => -1 - * 'foo' <=> 'FOO' # => 1 - * 'foo' <=> 1 # => nil + * 'a' <=> 'b' # => -1 + * 'a' <=> 'ab' # => -1 + * 'a' <=> 'a' # => 0 + * 'b' <=> 'a' # => 1 + * 'ab' <=> 'a' # => 1 + * 'a' <=> :a # => nil * * Related: see {Comparing}[rdoc-ref:String@Comparing]. */ From ed0fae5b501dea14617103ab5ec2d042f445ed7f Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 18 Dec 2025 20:00:13 -0500 Subject: [PATCH 5/6] [ruby/mmtk] Extract heap count to MMTK_HEAP_COUNT macro https://github.com/ruby/mmtk/commit/4e789e118b --- gc/mmtk/mmtk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index e1678dcf6ab0b4..58691e5a656b59 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -485,7 +485,9 @@ rb_gc_impl_init(void) rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1); } -static size_t heap_sizes[6] = { +#define MMTK_HEAP_COUNT 5 + +static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = { 40, 80, 160, 320, 640, 0 }; @@ -610,7 +612,7 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags struct MMTk_ractor_cache *ractor_cache = cache_ptr; if (alloc_size > 640) rb_bug("too big"); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < MMTK_HEAP_COUNT; i++) { if (alloc_size == heap_sizes[i]) break; if (alloc_size < heap_sizes[i]) { alloc_size = heap_sizes[i]; From 8274c5e1428b5b88e885857d466822cbadc19761 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 18 Dec 2025 20:01:13 -0500 Subject: [PATCH 6/6] [ruby/mmtk] Extract max object size to MMTK_MAX_OBJ_SIZE https://github.com/ruby/mmtk/commit/ed9036c295 --- gc/mmtk/mmtk.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 58691e5a656b59..131aaf38b0fd27 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -462,6 +462,13 @@ void rb_gc_impl_set_params(void *objspace_ptr) { } static VALUE gc_verify_internal_consistency(VALUE self) { return Qnil; } +#define MMTK_HEAP_COUNT 5 +#define MMTK_MAX_OBJ_SIZE 640 + +static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = { + 40, 80, 160, 320, MMTK_MAX_OBJ_SIZE, 0 +}; + void rb_gc_impl_init(void) { @@ -469,7 +476,7 @@ rb_gc_impl_init(void) rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(sizeof(VALUE) * 5)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic))); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(640)); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MMTK_MAX_OBJ_SIZE)); // Pretend we have 5 size pools rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(5)); OBJ_FREEZE(gc_constants); @@ -485,12 +492,6 @@ rb_gc_impl_init(void) rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1); } -#define MMTK_HEAP_COUNT 5 - -static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = { - 40, 80, 160, 320, 640, 0 -}; - size_t * rb_gc_impl_heap_sizes(void *objspace_ptr) { @@ -611,7 +612,7 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags struct objspace *objspace = objspace_ptr; struct MMTk_ractor_cache *ractor_cache = cache_ptr; - if (alloc_size > 640) rb_bug("too big"); + if (alloc_size > MMTK_MAX_OBJ_SIZE) rb_bug("too big"); for (int i = 0; i < MMTK_HEAP_COUNT; i++) { if (alloc_size == heap_sizes[i]) break; if (alloc_size < heap_sizes[i]) { @@ -660,7 +661,7 @@ rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size) bool rb_gc_impl_size_allocatable_p(size_t size) { - return size <= 640; + return size <= MMTK_MAX_OBJ_SIZE; } // Malloc